aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main
diff options
context:
space:
mode:
authorStas Vilchik <stas-vilchik@users.noreply.github.com>2017-05-30 16:40:23 +0200
committerGitHub <noreply@github.com>2017-05-30 16:40:23 +0200
commit862b59c20a2012f84acc4abf7531cb2fa2b1f3ec (patch)
tree880652733976f7dbaf074d70d97f1a373e2805d2 /server/sonar-web/src/main
parent88b3c97acd9154a1dc16bd1e158a02f2020ca6c8 (diff)
downloadsonarqube-862b59c20a2012f84acc4abf7531cb2fa2b1f3ec.tar.gz
sonarqube-862b59c20a2012f84acc4abf7531cb2fa2b1f3ec.zip
rework quality profiles modals (#2123)
Diffstat (limited to 'server/sonar-web/src/main')
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/App.js2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js7
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/CopyProfileForm.js134
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/DeleteProfileForm.js120
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js124
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/RenameProfileForm.js134
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.js141
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeProjectsForm.js (renamed from server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js)72
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js41
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js31
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.js197
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js96
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.js159
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-profile-parent.hbs21
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-projects.hbs12
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-copy-profile.hbs16
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs34
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-delete-profile.hbs18
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-rename-profile.hbs16
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs37
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js68
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js52
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js95
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js55
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js51
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js57
33 files changed, 1147 insertions, 657 deletions
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js
index 564996b4c5c..e60087cd0fc 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js
@@ -30,6 +30,7 @@ type Props = {
children: React.Element<*>,
currentUser: { permissions: { global: Array<string> } },
languages: Array<*>,
+ onRequestFail: Object => void,
organization: { name: string, canAdmin?: boolean, key: string } | null
};
@@ -108,6 +109,7 @@ export default class App extends React.PureComponent {
languages: finalLanguages,
exporters: this.state.exporters,
updateProfiles: this.updateProfiles,
+ onRequestFail: this.props.onRequestFail,
organization: organization ? organization.key : null,
canAdmin
});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js
index b5ff40a9ace..a9809ffe0dc 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js
@@ -20,6 +20,7 @@
import { connect } from 'react-redux';
import App from './App';
import { getLanguages, getCurrentUser, getOrganizationByKey } from '../../../store/rootReducer';
+import { onFail } from '../../../store/rootActions';
const mapStateToProps = (state, ownProps) => ({
currentUser: getCurrentUser(state),
@@ -29,4 +30,8 @@ const mapStateToProps = (state, ownProps) => ({
: null
});
-export default connect(mapStateToProps)(App);
+const mapDispatchToProps = dispatch => ({
+ onRequestFail: error => onFail(dispatch)(error)
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(App);
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/CopyProfileForm.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/CopyProfileForm.js
new file mode 100644
index 00000000000..3ced39bc3e3
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/CopyProfileForm.js
@@ -0,0 +1,134 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+// @flow
+import React from 'react';
+import Modal from 'react-modal';
+import type { Profile } from '../propTypes';
+import { copyProfile } from '../../../api/quality-profiles';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+
+type Props = {
+ onClose: () => void,
+ onCopy: string => void,
+ onRequestFail: Object => void,
+ profile: Profile
+};
+
+type State = {
+ loading: boolean,
+ name: ?string
+};
+
+export default class CopyProfileForm extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
+ state: State = { loading: false, name: null };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleCancelClick = (event: Event) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleNameChange = (event: { currentTarget: HTMLInputElement }) => {
+ this.setState({ name: event.currentTarget.value });
+ };
+
+ handleFormSubmit = (event: Event) => {
+ event.preventDefault();
+
+ const { name } = this.state;
+
+ if (name != null) {
+ this.setState({ loading: true });
+ copyProfile(this.props.profile.key, name).then(
+ profile => this.props.onCopy(profile.name),
+ error => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ this.props.onRequestFail(error);
+ }
+ );
+ }
+ };
+
+ render() {
+ const { profile } = this.props;
+ const header = translateWithParameters(
+ 'quality_profiles.copy_x_title',
+ profile.name,
+ profile.languageName
+ );
+ const submitDisabled =
+ this.state.loading || !this.state.name || this.state.name === profile.name;
+
+ return (
+ <Modal
+ isOpen={true}
+ contentLabel={header}
+ className="modal"
+ overlayClassName="modal-overlay"
+ onRequestClose={this.props.onClose}>
+
+ <form id="copy-profile-form" onSubmit={this.handleFormSubmit}>
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+ <div className="modal-body">
+ <div className="modal-field">
+ <label htmlFor="copy-profile-name">
+ {translate('quality_profiles.copy_new_name')}<em className="mandatory">*</em>
+ </label>
+ <input
+ autoFocus={true}
+ id="copy-profile-name"
+ maxLength="100"
+ name="name"
+ onChange={this.handleNameChange}
+ required={true}
+ size="50"
+ type="text"
+ value={this.state.name != null ? this.state.name : profile.name}
+ />
+ </div>
+ </div>
+ <div className="modal-foot">
+ {this.state.loading && <i className="spinner spacer-right" />}
+ <button disabled={submitDisabled} id="copy-profile-submit">
+ {translate('copy')}
+ </button>
+ <a href="#" id="copy-profile-cancel" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </div>
+ </form>
+
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/DeleteProfileForm.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/DeleteProfileForm.js
new file mode 100644
index 00000000000..03ca6da00cd
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/DeleteProfileForm.js
@@ -0,0 +1,120 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+// @flow
+import React from 'react';
+import Modal from 'react-modal';
+import type { Profile } from '../propTypes';
+import { deleteProfile } from '../../../api/quality-profiles';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+
+type Props = {
+ onClose: () => void,
+ onDelete: () => void,
+ onRequestFail: Object => void,
+ profile: Profile
+};
+
+type State = {
+ loading: boolean
+};
+
+export default class DeleteProfileForm extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
+ state: State = { loading: false, name: null };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleCancelClick = (event: Event) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleFormSubmit = (event: Event) => {
+ event.preventDefault();
+ this.setState({ loading: true });
+ deleteProfile(this.props.profile.key).then(this.props.onDelete, error => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ this.props.onRequestFail(error);
+ });
+ };
+
+ render() {
+ const { profile } = this.props;
+ const header = translate('quality_profiles.delete_confirm_title');
+
+ return (
+ <Modal
+ isOpen={true}
+ contentLabel={header}
+ className="modal"
+ overlayClassName="modal-overlay"
+ onRequestClose={this.props.onClose}>
+
+ <form id="delete-profile-form" onSubmit={this.handleFormSubmit}>
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+ <div className="modal-body">
+ <div className="js-modal-messages" />
+ {profile.childrenCount > 0
+ ? <div>
+ <div className="alert alert-warning">
+ {translate('quality_profiles.this_profile_has_descendants')}
+ </div>
+ <p>
+ {translateWithParameters(
+ 'quality_profiles.are_you_sure_want_delete_profile_x_and_descendants',
+ profile.name,
+ profile.languageName
+ )}
+ </p>
+ </div>
+ : <p>
+ {translateWithParameters(
+ 'quality_profiles.are_you_sure_want_delete_profile_x',
+ profile.name,
+ profile.languageName
+ )}
+ </p>}
+ </div>
+ <div className="modal-foot">
+ {this.state.loading && <i className="spinner spacer-right" />}
+ <button className="button-red" disabled={this.state.loading} id="delete-profile-submit">
+ {translate('delete')}
+ </button>
+ <a href="#" id="delete-profile-cancel" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </div>
+ </form>
+
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js
index ec8f51ee0a0..af02d8d9539 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js
@@ -20,9 +20,9 @@
// @flow
import React from 'react';
import { Link } from 'react-router';
-import RenameProfileView from '../views/RenameProfileView';
-import CopyProfileView from '../views/CopyProfileView';
-import DeleteProfileView from '../views/DeleteProfileView';
+import RenameProfileForm from './RenameProfileForm';
+import CopyProfileForm from './CopyProfileForm';
+import DeleteProfileForm from './DeleteProfileForm';
import { translate } from '../../../helpers/l10n';
import { getRulesUrl } from '../../../helpers/urls';
import { setDefaultProfile } from '../../../api/quality-profiles';
@@ -32,13 +32,21 @@ import type { Profile } from '../propTypes';
type Props = {
canAdmin: boolean,
fromList: boolean,
+ onRequestFail: Object => void,
organization: ?string,
profile: Profile,
updateProfiles: () => Promise<*>
};
+type State = {
+ copyFormOpen: boolean,
+ deleteFormOpen: boolean,
+ renameFormOpen: boolean
+};
+
export default class ProfileActions extends React.PureComponent {
props: Props;
+ state: State;
static defaultProps = {
fromList: false
@@ -48,32 +56,50 @@ export default class ProfileActions extends React.PureComponent {
router: React.PropTypes.object
};
- handleRenameClick = (e: SyntheticInputEvent) => {
- e.preventDefault();
- new RenameProfileView({ profile: this.props.profile })
- .on('done', (newName: string) => {
- this.props.updateProfiles().then(() => {
- if (!this.props.fromList) {
- this.context.router.replace(
- getProfilePath(newName, this.props.profile.language, this.props.organization)
- );
- }
- });
- })
- .render();
+ constructor(props: Props) {
+ super(props);
+ this.state = {
+ copyFormOpen: false,
+ deleteFormOpen: false,
+ renameFormOpen: false
+ };
+ }
+
+ handleRenameClick = (event: Event) => {
+ event.preventDefault();
+ this.setState({ renameFormOpen: true });
};
- handleCopyClick = (e: SyntheticInputEvent) => {
- e.preventDefault();
- new CopyProfileView({ profile: this.props.profile })
- .on('done', profile => {
- this.props.updateProfiles().then(() => {
- this.context.router.push(
- getProfilePath(profile.name, profile.language, this.props.organization)
- );
- });
- })
- .render();
+ handleProfileRename = (name: string) => {
+ this.closeRenameForm();
+ this.props.updateProfiles().then(() => {
+ if (!this.props.fromList) {
+ this.context.router.replace(
+ getProfilePath(name, this.props.profile.language, this.props.organization)
+ );
+ }
+ });
+ };
+
+ closeRenameForm = () => {
+ this.setState({ renameFormOpen: false });
+ };
+
+ handleCopyClick = (event: Event) => {
+ event.preventDefault();
+ this.setState({ copyFormOpen: true });
+ };
+
+ handleProfileCopy = (name: string) => {
+ this.props.updateProfiles().then(() => {
+ this.context.router.push(
+ getProfilePath(name, this.props.profile.language, this.props.organization)
+ );
+ });
+ };
+
+ closeCopyForm = () => {
+ this.setState({ copyFormOpen: false });
};
handleSetDefaultClick = (e: SyntheticInputEvent) => {
@@ -81,14 +107,18 @@ export default class ProfileActions extends React.PureComponent {
setDefaultProfile(this.props.profile.key).then(this.props.updateProfiles);
};
- handleDeleteClick = (e: SyntheticInputEvent) => {
- e.preventDefault();
- new DeleteProfileView({ profile: this.props.profile })
- .on('done', () => {
- this.context.router.replace(getProfilesPath(this.props.organization));
- this.props.updateProfiles();
- })
- .render();
+ handleDeleteClick = (event: Event) => {
+ event.preventDefault();
+ this.setState({ deleteFormOpen: true });
+ };
+
+ handleProfileDelete = () => {
+ this.context.router.replace(getProfilesPath(this.props.organization));
+ this.props.updateProfiles();
+ };
+
+ closeDeleteForm = () => {
+ this.setState({ deleteFormOpen: false });
};
render() {
@@ -152,6 +182,30 @@ export default class ProfileActions extends React.PureComponent {
{translate('delete')}
</a>
</li>}
+
+ {this.state.copyFormOpen &&
+ <CopyProfileForm
+ onClose={this.closeCopyForm}
+ onCopy={this.handleProfileCopy}
+ onRequestFail={this.props.onRequestFail}
+ profile={profile}
+ />}
+
+ {this.state.deleteFormOpen &&
+ <DeleteProfileForm
+ onClose={this.closeDeleteForm}
+ onDelete={this.handleProfileDelete}
+ onRequestFail={this.props.onRequestFail}
+ profile={profile}
+ />}
+
+ {this.state.renameFormOpen &&
+ <RenameProfileForm
+ onClose={this.closeRenameForm}
+ onRename={this.handleProfileRename}
+ onRequestFail={this.props.onRequestFail}
+ profile={profile}
+ />}
</ul>
);
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js
index ed1d683ed1c..ceda3d91f5f 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js
@@ -31,6 +31,7 @@ type Props = {
pathname: string,
query: { key?: string, language: string, name: string }
},
+ onRequestFail: Object => void,
organization: ?string,
profiles: Array<Profile>,
router: { replace: () => void },
@@ -78,6 +79,7 @@ export default class ProfileContainer extends React.PureComponent {
}
const child = React.cloneElement(this.props.children, {
+ onRequestFail: this.props.onRequestFail,
organization,
profile,
profiles,
@@ -89,6 +91,7 @@ export default class ProfileContainer extends React.PureComponent {
<Helmet title={profile.name} />
<ProfileHeader
canAdmin={this.props.canAdmin}
+ onRequestFail={this.props.onRequestFail}
organization={organization}
profile={profile}
updateProfiles={this.props.updateProfiles}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/RenameProfileForm.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/RenameProfileForm.js
new file mode 100644
index 00000000000..9a2af870205
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/RenameProfileForm.js
@@ -0,0 +1,134 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+// @flow
+import React from 'react';
+import Modal from 'react-modal';
+import type { Profile } from '../propTypes';
+import { renameProfile } from '../../../api/quality-profiles';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+
+type Props = {
+ onClose: () => void,
+ onRename: string => void,
+ onRequestFail: Object => void,
+ profile: Profile
+};
+
+type State = {
+ loading: boolean,
+ name: ?string
+};
+
+export default class RenameProfileForm extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
+ state: State = { loading: false, name: null };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleCancelClick = (event: Event) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleNameChange = (event: { currentTarget: HTMLInputElement }) => {
+ this.setState({ name: event.currentTarget.value });
+ };
+
+ handleFormSubmit = (event: Event) => {
+ event.preventDefault();
+
+ const { name } = this.state;
+
+ if (name != null) {
+ this.setState({ loading: true });
+ renameProfile(this.props.profile.key, name).then(
+ () => this.props.onRename(name),
+ error => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ this.props.onRequestFail(error);
+ }
+ );
+ }
+ };
+
+ render() {
+ const { profile } = this.props;
+ const header = translateWithParameters(
+ 'quality_profiles.rename_x_title',
+ profile.name,
+ profile.languageName
+ );
+ const submitDisabled =
+ this.state.loading || !this.state.name || this.state.name === profile.name;
+
+ return (
+ <Modal
+ isOpen={true}
+ contentLabel={header}
+ className="modal"
+ overlayClassName="modal-overlay"
+ onRequestClose={this.props.onClose}>
+
+ <form id="rename-profile-form" onSubmit={this.handleFormSubmit}>
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+ <div className="modal-body">
+ <div className="modal-field">
+ <label htmlFor="rename-profile-name">
+ {translate('quality_profiles.new_name')}<em className="mandatory">*</em>
+ </label>
+ <input
+ autoFocus={true}
+ id="rename-profile-name"
+ maxLength="100"
+ name="name"
+ onChange={this.handleNameChange}
+ required={true}
+ size="50"
+ type="text"
+ value={this.state.name != null ? this.state.name : profile.name}
+ />
+ </div>
+ </div>
+ <div className="modal-foot">
+ {this.state.loading && <i className="spinner spacer-right" />}
+ <button disabled={submitDisabled} id="rename-profile-submit">
+ {translate('rename')}
+ </button>
+ <a href="#" id="rename-profile-cancel" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </div>
+ </form>
+
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.js
new file mode 100644
index 00000000000..5f0aeb88690
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.js
@@ -0,0 +1,141 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+// @flow
+import React from 'react';
+import Modal from 'react-modal';
+import Select from 'react-select';
+import { sortBy } from 'lodash';
+import { changeProfileParent } from '../../../api/quality-profiles';
+import { translate } from '../../../helpers/l10n';
+import type { Profile } from '../propTypes';
+
+type Props = {
+ onChange: () => void,
+ onClose: () => void,
+ onRequestFail: Object => void,
+ profile: Profile,
+ profiles: Array<Profile>
+};
+
+type State = {
+ loading: boolean,
+ selected: ?string
+};
+
+export default class ChangeParentForm extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
+ state: State = {
+ loading: false,
+ selected: null
+ };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleCancelClick = (event: Event) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleSelectChange = (option: { value: string }) => {
+ this.setState({ selected: option.value });
+ };
+
+ handleFormSubmit = (event: Event) => {
+ event.preventDefault();
+
+ const parent = this.state.selected;
+
+ if (parent != null) {
+ this.setState({ loading: true });
+ changeProfileParent(this.props.profile.key, parent).then(this.props.onChange).catch(error => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ this.props.onRequestFail(error);
+ });
+ }
+ };
+
+ render() {
+ const { profiles } = this.props;
+
+ const options = [
+ { label: translate('none'), value: '' },
+ ...sortBy(profiles, 'name').map(profile => ({ label: profile.name, value: profile.key }))
+ ];
+
+ const submitDisabled =
+ this.state.loading ||
+ this.state.selected == null ||
+ this.state.selected === this.props.profile.parentKey;
+
+ return (
+ <Modal
+ isOpen={true}
+ contentLabel={translate('quality_profiles.change_parent')}
+ className="modal"
+ overlayClassName="modal-overlay"
+ onRequestClose={this.props.onClose}>
+
+ <form id="change-profile-parent-form" onSubmit={this.handleFormSubmit}>
+ <div className="modal-head">
+ <h2>{translate('quality_profiles.change_parent')}</h2>
+ </div>
+ <div className="modal-body">
+ <div className="modal-field">
+ <label htmlFor="change-profile-parent">
+ {translate('quality_profiles.parent')}: <em className="mandatory">*</em>
+ </label>
+ <Select
+ clearable={false}
+ id="change-profile-parent"
+ name="parentKey"
+ onChange={this.handleSelectChange}
+ options={options}
+ value={
+ this.state.selected != null
+ ? this.state.selected
+ : this.props.profile.parentKey || ''
+ }
+ />
+ </div>
+ </div>
+ <div className="modal-foot">
+ {this.state.loading && <i className="spinner spacer-right" />}
+ <button disabled={submitDisabled} id="change-profile-parent-submit">
+ {translate('change_verb')}
+ </button>
+ <a href="#" id="change-profile-parent-cancel" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </div>
+ </form>
+
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeProjectsForm.js
index b9601e3eb5c..6f46189d3e3 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeProjectsForm.js
@@ -18,39 +18,47 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
+import React from 'react';
+import Modal from 'react-modal';
import escapeHtml from 'escape-html';
-import ModalFormView from '../../../components/common/modal-form';
-import Template from '../templates/quality-profiles-change-projects.hbs';
+import type { Profile } from '../propTypes';
import { translate } from '../../../helpers/l10n';
-import '../../../components/SelectList';
-export default ModalFormView.extend({
- template: Template,
+type Props = {
+ onClose: () => void,
+ organization: ?string,
+ profile: Profile
+};
- onRender() {
- // TODO remove uuid usage
+export default class ChangeProjectsForm extends React.PureComponent {
+ container: HTMLElement;
+ props: Props;
- ModalFormView.prototype.onRender.apply(this, arguments);
+ componentDidMount() {
+ this.renderSelectList();
+ }
+
+ handleCloseClick = (event: Event) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
- const { key } = this.options.profile;
+ renderSelectList() {
+ const { key } = this.props.profile;
const searchUrl =
window.baseUrl + '/api/qualityprofiles/projects?key=' + encodeURIComponent(key);
new window.SelectList({
searchUrl,
- el: this.$('#profile-projects'),
+ el: this.container,
width: '100%',
readOnly: false,
focusSearch: false,
- dangerouslyUnescapedHtmlFormat(item) {
- return escapeHtml(item.name);
- },
+ dangerouslyUnescapedHtmlFormat: item => escapeHtml(item.name),
selectUrl: window.baseUrl + '/api/qualityprofiles/add_project',
deselectUrl: window.baseUrl + '/api/qualityprofiles/remove_project',
- extra: {
- profileKey: key
- },
+ extra: { profileKey: key },
selectParameter: 'projectUuid',
selectParameterValue: 'uuid',
labels: {
@@ -64,10 +72,32 @@ export default ModalFormView.extend({
deselect: translate('quality_profiles.projects.deselect_hint')
}
});
- },
+ }
+
+ render() {
+ const header = translate('projects');
+
+ return (
+ <Modal
+ isOpen={true}
+ contentLabel={header}
+ className="modal"
+ overlayClassName="modal-overlay"
+ onRequestClose={this.props.onClose}>
+
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+
+ <div className="modal-body">
+ <div id="profile-projects" ref={node => (this.container = node)} />
+ </div>
+
+ <div className="modal-foot">
+ <a href="#" onClick={this.handleCloseClick}>{translate('close')}</a>
+ </div>
- onDestroy() {
- this.options.loadProjects();
- ModalFormView.prototype.onDestroy.apply(this, arguments);
+ </Modal>
+ );
}
-});
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js
index 7b7a296536c..9417f4cf0c9 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js
@@ -28,6 +28,7 @@ import type { Profile, Exporter } from '../propTypes';
type Props = {
canAdmin: boolean,
exporters: Array<Exporter>,
+ onRequestFail: Object => void,
organization: ?string,
profile: Profile,
profiles: Array<Profile>,
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js
index 5c66dfcfcac..02e1b508ff2 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js
@@ -34,6 +34,7 @@ import type { Profile } from '../propTypes';
type Props = {
canAdmin: boolean,
+ onRequestFail: Object => void,
organization: ?string,
profile: Profile,
updateProfiles: () => Promise<*>
@@ -136,6 +137,7 @@ export default class ProfileHeader extends React.PureComponent {
</button>
<ProfileActions
canAdmin={this.props.canAdmin}
+ onRequestFail={this.props.onRequestFail}
organization={organization}
profile={profile}
updateProfiles={this.props.updateProfiles}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js
index cc8a8a9f535..ba11b5a9e5d 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js
@@ -21,13 +21,14 @@
import React from 'react';
import classNames from 'classnames';
import ProfileInheritanceBox from './ProfileInheritanceBox';
-import ChangeParentView from '../views/ChangeParentView';
+import ChangeParentForm from './ChangeParentForm';
import { translate } from '../../../helpers/l10n';
import { getProfileInheritance } from '../../../api/quality-profiles';
import type { Profile } from '../propTypes';
type Props = {
canAdmin: boolean,
+ onRequestFail: Object => void,
organization: ?string,
profile: Profile,
profiles: Array<Profile>,
@@ -45,6 +46,7 @@ type ProfileInheritanceDetails = {
type State = {
ancestors?: Array<ProfileInheritanceDetails>,
children?: Array<ProfileInheritanceDetails>,
+ formOpen: boolean,
loading: boolean,
profile?: ProfileInheritanceDetails
};
@@ -53,6 +55,7 @@ export default class ProfileInheritance extends React.PureComponent {
mounted: boolean;
props: Props;
state: State = {
+ formOpen: false,
loading: true
};
@@ -85,14 +88,23 @@ export default class ProfileInheritance extends React.PureComponent {
});
}
- handleChangeParent = (e: SyntheticInputEvent) => {
- e.preventDefault();
- new ChangeParentView({ profile: this.props.profile, profiles: this.props.profiles })
- .on('done', () => this.props.updateProfiles())
- .render();
+ handleChangeParentClick = (event: Event) => {
+ event.preventDefault();
+ this.setState({ formOpen: true });
+ };
+
+ closeForm = () => {
+ this.setState({ formOpen: false });
+ };
+
+ handleParentChange = () => {
+ this.props.updateProfiles();
+ this.closeForm();
};
render() {
+ const { profile, profiles } = this.props;
+
const highlightCurrent =
!this.state.loading &&
this.state.ancestors != null &&
@@ -109,7 +121,7 @@ export default class ProfileInheritance extends React.PureComponent {
{translate('quality_profiles.profile_inheritance')}
</h2>
{this.props.canAdmin &&
- <button className="pull-right js-change-parent" onClick={this.handleChangeParent}>
+ <button className="pull-right js-change-parent" onClick={this.handleChangeParentClick}>
{translate('quality_profiles.change_parent')}
</button>}
</header>
@@ -123,7 +135,7 @@ export default class ProfileInheritance extends React.PureComponent {
className="js-inheritance-ancestor"
depth={index}
key={ancestor.key}
- language={this.props.profile.language}
+ language={profile.language}
organization={this.props.organization}
profile={ancestor}
/>
@@ -133,7 +145,7 @@ export default class ProfileInheritance extends React.PureComponent {
className={currentClassName}
depth={this.state.ancestors ? this.state.ancestors.length : 0}
displayLink={false}
- language={this.props.profile.language}
+ language={profile.language}
organization={this.props.organization}
profile={this.state.profile}
/>
@@ -144,13 +156,22 @@ export default class ProfileInheritance extends React.PureComponent {
className="js-inheritance-child"
depth={this.state.ancestors ? this.state.ancestors.length + 1 : 0}
key={child.key}
- language={this.props.profile.language}
+ language={profile.language}
organization={this.props.organization}
profile={child}
/>
))}
</tbody>
</table>}
+
+ {this.state.formOpen &&
+ <ChangeParentForm
+ onChange={this.handleParentChange}
+ onClose={this.closeForm}
+ onRequestFail={this.props.onRequestFail}
+ profile={profile}
+ profiles={profiles.filter(p => p !== profile && p.language === profile.language)}
+ />}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js
index 171d4ab8e0d..e3b08402dd6 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js
@@ -20,7 +20,7 @@
// @flow
import React from 'react';
import { Link } from 'react-router';
-import ChangeProjectsView from '../views/ChangeProjectsView';
+import ChangeProjectsForm from './ChangeProjectsForm';
import QualifierIcon from '../../../components/shared/QualifierIcon';
import { getProfileProjects } from '../../../api/quality-profiles';
import { translate } from '../../../helpers/l10n';
@@ -34,6 +34,7 @@ type Props = {
};
type State = {
+ formOpen: boolean,
loading: boolean,
more?: boolean,
projects: ?Array<*>
@@ -43,6 +44,7 @@ export default class ProfileProjects extends React.PureComponent {
mounted: boolean;
props: Props;
state: State = {
+ formOpen: false,
loading: true,
projects: null
};
@@ -79,15 +81,15 @@ export default class ProfileProjects extends React.PureComponent {
});
}
- handleChange(e: SyntheticInputEvent) {
- e.preventDefault();
- e.target.blur();
- new ChangeProjectsView({
- loadProjects: this.props.updateProfiles,
- organization: this.props.organization,
- profile: this.props.profile
- }).render();
- }
+ handleChangeClick = (event: Event) => {
+ event.preventDefault();
+ this.setState({ formOpen: true });
+ };
+
+ closeForm = () => {
+ this.setState({ formOpen: false });
+ this.props.updateProfiles();
+ };
renderDefault() {
return (
@@ -143,13 +145,20 @@ export default class ProfileProjects extends React.PureComponent {
{this.props.canAdmin &&
!this.props.profile.isDefault &&
<div className="pull-right">
- <button className="js-change-projects" onClick={this.handleChange.bind(this)}>
+ <button className="js-change-projects" onClick={this.handleChangeClick}>
{translate('quality_profiles.change_projects')}
</button>
</div>}
</header>
{this.props.profile.isDefault ? this.renderDefault() : this.renderProjects()}
+
+ {this.state.formOpen &&
+ <ChangeProjectsForm
+ onClose={this.closeForm}
+ organization={this.props.organization}
+ profile={this.props.profile}
+ />}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.js
new file mode 100644
index 00000000000..4219dbd37f6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.js
@@ -0,0 +1,197 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+// @flow
+import React from 'react';
+import Modal from 'react-modal';
+import Select from 'react-select';
+import { sortBy } from 'lodash';
+import { getImporters, createQualityProfile } from '../../../api/quality-profiles';
+import { translate } from '../../../helpers/l10n';
+
+type Props = {
+ languages: Array<{ key: string, name: string }>,
+ onClose: () => void,
+ onCreate: Function,
+ onRequestFail: Object => void,
+ organization: ?string
+};
+
+type State = {
+ importers: Array<{ key: string, languages: Array<string>, name: string }>,
+ language?: string,
+ loading: boolean,
+ name: string,
+ preloading: boolean
+};
+
+export default class CreateProfileForm extends React.PureComponent {
+ form: HTMLFormElement;
+ mounted: boolean;
+ props: Props;
+ state: State = { importers: [], loading: false, name: '', preloading: true };
+
+ componentDidMount() {
+ this.mounted = true;
+ this.fetchImporters();
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ fetchImporters() {
+ getImporters().then(importers => {
+ if (this.mounted) {
+ this.setState({ importers, preloading: false });
+ }
+ });
+ }
+
+ handleCancelClick = (event: Event) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleNameChange = (event: { currentTarget: HTMLInputElement }) => {
+ this.setState({ name: event.currentTarget.value });
+ };
+
+ handleLanguageChange = (option: { value: string }) => {
+ this.setState({ language: option.value });
+ };
+
+ handleFormSubmit = (event: Event) => {
+ event.preventDefault();
+
+ this.setState({ loading: true });
+
+ const data = new FormData(this.form);
+ if (this.props.organization) {
+ data.append('organization', this.props.organization);
+ }
+
+ createQualityProfile(data).then(
+ response => this.props.onCreate(response.profile),
+ error => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ this.props.onRequestFail(error);
+ }
+ );
+ };
+
+ render() {
+ const header = translate('quality_profiles.new_profile');
+
+ const languages = sortBy(this.props.languages, 'name');
+ const selectedLanguage = this.state.language || languages[0].key;
+ const importers = this.state.importers.filter(importer =>
+ importer.languages.includes(selectedLanguage)
+ );
+
+ return (
+ <Modal
+ isOpen={true}
+ contentLabel={header}
+ className="modal"
+ overlayClassName="modal-overlay"
+ onRequestClose={this.props.onClose}>
+
+ <form
+ id="create-profile-form"
+ onSubmit={this.handleFormSubmit}
+ ref={node => (this.form = node)}>
+
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+
+ {this.state.preloading
+ ? <div className="modal-body"><i className="spinner" /></div>
+ : <div className="modal-body">
+ <div className="modal-field">
+ <label htmlFor="create-profile-name">
+ {translate('name')}<em className="mandatory">*</em>
+ </label>
+ <input
+ autoFocus={true}
+ id="create-profile-name"
+ maxLength="100"
+ name="name"
+ onChange={this.handleNameChange}
+ required={true}
+ size="50"
+ type="text"
+ value={this.state.name}
+ />
+ </div>
+ <div className="modal-field spacer-bottom">
+ <label htmlFor="create-profile-language">
+ {translate('language')}<em className="mandatory">*</em>
+ </label>
+ <Select
+ clearable={false}
+ id="create-profile-language"
+ name="language"
+ onChange={this.handleLanguageChange}
+ options={languages.map(language => ({
+ label: language.name,
+ value: language.key
+ }))}
+ value={selectedLanguage}
+ />
+ </div>
+ {importers.map(importer => (
+ <div
+ className="modal-field spacer-bottom js-importer"
+ data-key={importer.key}
+ key={importer.key}>
+ <label htmlFor={'create-profile-form-backup-' + importer.key}>
+ {importer.name}
+ </label>
+ <input
+ id={'create-profile-form-backup-' + importer.key}
+ name={'backup_' + importer.key}
+ type="file"
+ />
+ <p className="note">
+ {translate('quality_profiles.optional_configuration_file')}
+ </p>
+ </div>
+ ))}
+ </div>}
+
+ <div className="modal-foot">
+ {this.state.loading && <i className="spinner spacer-right" />}
+ {!this.state.preloading &&
+ <button disabled={this.state.loading} id="create-profile-submit">
+ {translate('create')}
+ </button>}
+ <a href="#" id="create-profile-cancel" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </div>
+ </form>
+
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js
index df44389a9c6..70389154ed4 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js
@@ -28,6 +28,7 @@ type Props = {
canAdmin: boolean,
languages: Array<{ key: string, name: string }>,
location: { query: { [string]: string } },
+ onRequestFail: Object => void,
organization?: string,
profiles: Array<Profile>,
updateProfiles: () => Promise<*>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js
index df6191a8300..6a276e243ee 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js
@@ -19,76 +19,63 @@
*/
// @flow
import React from 'react';
-import CreateProfileView from '../views/CreateProfileView';
-import RestoreProfileView from '../views/RestoreProfileView';
+import CreateProfileForm from './CreateProfileForm';
+import RestoreProfileForm from './RestoreProfileForm';
import RestoreBuiltInProfilesView from '../views/RestoreBuiltInProfilesView';
+import type { Profile } from '../propTypes';
import { getProfilePath } from '../utils';
import { translate } from '../../../helpers/l10n';
-import { getImporters } from '../../../api/quality-profiles';
type Props = {
canAdmin: boolean,
languages: Array<{ key: string, name: string }>,
+ onRequestFail: Object => void,
organization: ?string,
updateProfiles: () => Promise<*>
};
+type State = {
+ createFormOpen: boolean,
+ restoreFormOpen: boolean
+};
+
export default class PageHeader extends React.PureComponent {
- mounted: boolean;
props: Props;
static contextTypes = {
router: React.PropTypes.object
};
- state = {};
+ state: State = {
+ createFormOpen: false,
+ restoreFormOpen: false
+ };
- componentDidMount() {
- this.mounted = true;
- }
+ handleCreateClick = (event: Event & { currentTarget: HTMLButtonElement }) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState({ createFormOpen: true });
+ };
- componentWillUnmount() {
- this.mounted = false;
- }
+ handleCreate = (profile: Profile) => {
+ this.props.updateProfiles().then(() => {
+ this.context.router.push(
+ getProfilePath(profile.name, profile.language, this.props.organization)
+ );
+ });
+ };
- retrieveImporters() {
- if (this.state.importers) {
- return Promise.resolve(this.state.importers);
- } else {
- return getImporters().then(importers => {
- this.setState({ importers });
- return importers;
- });
- }
- }
+ closeCreateForm = () => {
+ this.setState({ createFormOpen: false });
+ };
- handleCreateClick = (e: SyntheticInputEvent) => {
- e.preventDefault();
- e.target.blur();
- this.retrieveImporters().then(importers => {
- new CreateProfileView({
- languages: this.props.languages,
- organization: this.props.organization,
- importers
- })
- .on('done', profile => {
- this.props.updateProfiles().then(() => {
- this.context.router.push(
- getProfilePath(profile.name, profile.language, this.props.organization)
- );
- });
- })
- .render();
- });
+ handleRestoreClick = (event: Event) => {
+ event.preventDefault();
+ this.setState({ restoreFormOpen: true });
};
- handleRestoreClick = (e: SyntheticInputEvent) => {
- e.preventDefault();
- new RestoreProfileView({
- organization: this.props.organization
- })
- .on('done', this.props.updateProfiles)
- .render();
+ closeRestoreForm = () => {
+ this.setState({ restoreFormOpen: false });
};
handleRestoreBuiltIn = (e: SyntheticInputEvent) => {
@@ -139,6 +126,23 @@ export default class PageHeader extends React.PureComponent {
<br />
{translate('quality_profiles.intro2')}
</div>
+
+ {this.state.restoreFormOpen &&
+ <RestoreProfileForm
+ onClose={this.closeRestoreForm}
+ onRequestFail={this.props.onRequestFail}
+ onRestore={this.props.updateProfiles}
+ organization={this.props.organization}
+ />}
+
+ {this.state.createFormOpen &&
+ <CreateProfileForm
+ languages={this.props.languages}
+ onClose={this.closeCreateForm}
+ onRequestFail={this.props.onRequestFail}
+ onCreate={this.handleCreate}
+ organization={this.props.organization}
+ />}
</header>
);
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js
index c2b0681f686..dbcf7840677 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js
@@ -30,6 +30,7 @@ type Props = {
canAdmin: boolean,
languages: Array<{ key: string, name: string }>,
location: { query: { [string]: string } },
+ onRequestFail: Object => void,
organization: ?string,
profiles: Array<Profile>,
updateProfiles: () => Promise<*>
@@ -43,6 +44,7 @@ export default class ProfilesList extends React.PureComponent {
<ProfilesListRow
canAdmin={this.props.canAdmin}
key={profile.key}
+ onRequestFail={this.props.onRequestFail}
organization={this.props.organization}
profile={profile}
updateProfiles={this.props.updateProfiles}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js
index 85dcb48c1d3..ec8bcf086e4 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js
@@ -30,6 +30,7 @@ import type { Profile } from '../propTypes';
type Props = {
canAdmin: boolean,
+ onRequestFail: Object => void,
organization: ?string,
profile: Profile,
updateProfiles: () => Promise<*>
@@ -160,6 +161,7 @@ export default class ProfilesListRow extends React.PureComponent {
<ProfileActions
canAdmin={this.props.canAdmin}
fromList={true}
+ onRequestFail={this.props.onRequestFail}
organization={this.props.organization}
profile={this.props.profile}
updateProfiles={this.props.updateProfiles}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.js
new file mode 100644
index 00000000000..911482ee914
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.js
@@ -0,0 +1,159 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+// @flow
+import React from 'react';
+import Modal from 'react-modal';
+import { restoreQualityProfile } from '../../../api/quality-profiles';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+
+type Props = {
+ onClose: () => void,
+ onRequestFail: Object => void,
+ onRestore: Function,
+ organization: ?string
+};
+
+type State = {
+ loading: boolean,
+ profile?: { name: string },
+ ruleFailures?: number,
+ ruleSuccesses?: number
+};
+
+export default class RestoreProfileForm extends React.PureComponent {
+ form: HTMLFormElement;
+ mounted: boolean;
+ props: Props;
+ state: State = { loading: false };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleCancelClick = (event: Event) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleFormSubmit = (event: Event) => {
+ event.preventDefault();
+
+ this.setState({ loading: true });
+
+ const data = new FormData(this.form);
+ if (this.props.organization) {
+ data.append('organization', this.props.organization);
+ }
+
+ restoreQualityProfile(data).then(
+ response => {
+ if (this.mounted) {
+ this.setState({
+ loading: false,
+ profile: response.profile,
+ ruleFailures: response.ruleFailures,
+ ruleSuccesses: response.ruleSuccesses
+ });
+ }
+ this.props.onRestore();
+ },
+ error => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ this.props.onRequestFail(error);
+ }
+ );
+ };
+
+ render() {
+ const header = translate('quality_profiles.restore_profile');
+
+ const { loading, profile, ruleFailures, ruleSuccesses } = this.state;
+
+ return (
+ <Modal
+ isOpen={true}
+ contentLabel={header}
+ className="modal"
+ overlayClassName="modal-overlay"
+ onRequestClose={this.props.onClose}>
+
+ <form
+ id="restore-profile-form"
+ onSubmit={this.handleFormSubmit}
+ ref={node => (this.form = node)}>
+
+ <div className="modal-head">
+ <h2>{header}</h2>
+ </div>
+
+ <div className="modal-body">
+ {profile != null && ruleSuccesses != null
+ ? ruleFailures
+ ? <div className="alert alert-warning">
+ {translateWithParameters(
+ 'quality_profiles.restore_profile.warning',
+ profile.name,
+ ruleSuccesses,
+ ruleFailures
+ )}
+ </div>
+ : <div className="alert alert-success">
+ {translateWithParameters(
+ 'quality_profiles.restore_profile.success',
+ profile.name,
+ ruleSuccesses
+ )}
+ </div>
+ : <div className="modal-field">
+ <label htmlFor="restore-profile-backup">
+ {translate('backup')}<em className="mandatory">*</em>
+ </label>
+ <input id="restore-profile-backup" name="backup" required={true} type="file" />
+ </div>}
+ </div>
+
+ {ruleSuccesses == null
+ ? <div className="modal-foot">
+ {loading && <i className="spinner spacer-right" />}
+ <button disabled={loading} id="restore-profile-submit">
+ {translate('restore')}
+ </button>
+ <a href="#" id="restore-profile-cancel" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </div>
+ : <div className="modal-foot">
+ <a href="#" onClick={this.handleCancelClick}>
+ {translate('close')}
+ </a>
+ </div>}
+
+ </form>
+
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js b/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js
index f8438011916..92af7067b7b 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js
@@ -37,7 +37,8 @@ export type Profile = {
userUpdatedAt?: string,
lastUsed?: string,
rulesUpdatedAt: string,
- depth: number
+ depth: number,
+ childrenCount: number
};
export type Exporter = {
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-profile-parent.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-profile-parent.hbs
deleted file mode 100644
index 94c7e29c60a..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-profile-parent.hbs
+++ /dev/null
@@ -1,21 +0,0 @@
-<form id="change-profile-parent-form">
- <div class="modal-head">
- <h2>{{t 'quality_profiles.change_parent'}}</h2>
- </div>
- <div class="modal-body">
- <div class="js-modal-messages"></div>
- <div class="modal-field">
- <label for="change-profile-parent">Parent: <em class="mandatory">*</em></label>
- <select id="change-profile-parent" name="parentKey">
- <option value="#none">{{t 'none'}}</option>
- {{#each profiles}}
- <option value="{{key}}">{{name}}</option>
- {{/each}}
- </select>
- </div>
- </div>
- <div class="modal-foot">
- <button id="change-profile-parent-submit">{{t 'change_verb'}}</button>
- <a href="#" class="js-modal-close" id="change-profile-parent-cancel">{{t 'cancel'}}</a>
- </div>
-</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-projects.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-projects.hbs
deleted file mode 100644
index 09e591327e2..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-change-projects.hbs
+++ /dev/null
@@ -1,12 +0,0 @@
-<div class="modal-head">
- <h2>{{t 'projects'}}</h2>
-</div>
-
-<div class="modal-body">
- <div class="js-modal-messages"></div>
- <div id="profile-projects"></div>
-</div>
-
-<div class="modal-foot">
- <a href="#" class="js-modal-close">{{t 'close'}}</a>
-</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-copy-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-copy-profile.hbs
deleted file mode 100644
index 6cc034f1209..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-copy-profile.hbs
+++ /dev/null
@@ -1,16 +0,0 @@
-<form id="copy-profile-form">
- <div class="modal-head">
- <h2>{{tp 'quality_profiles.copy_x_title' name languageName}}</h2>
- </div>
- <div class="modal-body">
- <div class="js-modal-messages"></div>
- <div class="modal-field">
- <label for="copy-profile-name">{{t 'quality_profiles.copy_new_name'}}<em class="mandatory">*</em></label>
- <input id="copy-profile-name" name="name" type="text" size="50" maxlength="100" required>
- </div>
- </div>
- <div class="modal-foot">
- <button id="copy-profile-submit">{{t 'copy'}}</button>
- <a href="#" class="js-modal-close" id="copy-profile-cancel">{{t 'cancel'}}</a>
- </div>
-</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs
deleted file mode 100644
index acdc7f93805..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs
+++ /dev/null
@@ -1,34 +0,0 @@
-<form id="create-profile-form" action="{{link '/api/qualityprofiles/create'}}" enctype="multipart/form-data" method="POST">
- <div class="modal-head">
- <h2>{{t 'quality_profiles.new_profile'}}</h2>
- </div>
- <div class="modal-body">
- <div class="js-modal-messages"></div>
- {{#if organization}}
- <input type="hidden" name="organization" value="{{organization}}">
- {{/if}}
- <div class="modal-field">
- <label for="create-profile-name">{{t 'name'}}<em class="mandatory">*</em></label>
- <input id="create-profile-name" name="name" type="text" size="50" maxlength="100" required>
- </div>
- <div class="modal-field spacer-bottom">
- <label for="create-profile-language">{{t 'language'}}<em class="mandatory">*</em></label>
- <select id="create-profile-language" name="language" required>
- {{#each languages}}
- <option value="{{key}}">{{name}}</option>
- {{/each}}
- </select>
- </div>
- {{#each importers}}
- <div class="modal-field spacer-bottom js-importer" data-key="{{key}}">
- <label for="create-profile-form-backup-{{key}}">{{name}}</label>
- <input id="create-profile-form-backup-{{key}}" name="backup_{{key}}" type="file">
- <p class="note">{{t 'quality_profiles.optional_configuration_file'}}</p>
- </div>
- {{/each}}
- </div>
- <div class="modal-foot">
- <button id="create-profile-submit">{{t 'create'}}</button>
- <a href="#" class="js-modal-close" id="create-profile-cancel">{{t 'cancel'}}</a>
- </div>
-</form> \ No newline at end of file
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-delete-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-delete-profile.hbs
deleted file mode 100644
index 17438526340..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-delete-profile.hbs
+++ /dev/null
@@ -1,18 +0,0 @@
-<form id="delete-profile-form">
- <div class="modal-head">
- <h2>{{t 'quality_profiles.delete_confirm_title'}}</h2>
- </div>
- <div class="modal-body">
- <div class="js-modal-messages"></div>
- {{#if childrenCount}}
- <div class="alert alert-warning">{{t 'quality_profiles.this_profile_has_descendants'}}</div>
- <p>{{tp 'quality_profiles.are_you_sure_want_delete_profile_x_and_descendants' name languageName}}</p>
- {{else}}
- <p>{{tp 'quality_profiles.are_you_sure_want_delete_profile_x' name languageName}}</p>
- {{/if}}
- </div>
- <div class="modal-foot">
- <button id="delete-profile-submit">{{t 'delete'}}</button>
- <a href="#" class="js-modal-close" id="delete-profile-cancel">{{t 'cancel'}}</a>
- </div>
-</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-rename-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-rename-profile.hbs
deleted file mode 100644
index 3da132f8f4f..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-rename-profile.hbs
+++ /dev/null
@@ -1,16 +0,0 @@
-<form id="rename-profile-form">
- <div class="modal-head">
- <h2>{{tp 'quality_profiles.rename_x_title' name languageName}}</h2>
- </div>
- <div class="modal-body">
- <div class="js-modal-messages"></div>
- <div class="modal-field">
- <label for="rename-profile-name">{{t 'quality_profiles.new_name'}} <em class="mandatory">*</em></label>
- <input id="rename-profile-name" name="name" type="text" size="50" maxlength="100" value="{{name}}" required>
- </div>
- </div>
- <div class="modal-foot">
- <button id="rename-profile-submit">{{t 'rename'}}</button>
- <a href="#" class="js-modal-close" id="rename-profile-cancel">{{t 'cancel'}}</a>
- </div>
-</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs
deleted file mode 100644
index 7c22c83aab5..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs
+++ /dev/null
@@ -1,37 +0,0 @@
-<form id="restore-profile-form">
- <div class="modal-head">
- <h2>{{t 'quality_profiles.restore_profile'}}</h2>
- </div>
-
- <div class="modal-body">
- <div class="js-modal-messages"></div>
- {{#if organization}}
- <input type="hidden" name="organization" value="{{organization}}">
- {{/if}}
- {{#if profile}}
- {{#if ruleFailures}}
- <div class="alert alert-warning">
- {{tp 'quality_profiles.restore_profile.warning' profile.name ruleSuccesses ruleFailures}}
- </div>
- {{else}}
- <div class="alert alert-success">
- {{tp 'quality_profiles.restore_profile.success' profile.name ruleSuccesses}}
- </div>
- {{/if}}
- {{else}}
- <div class="modal-field">
- <label for="restore-profile-backup">{{t 'backup'}}<em class="mandatory">*</em></label>
- <input type="file" id="restore-profile-backup" name="backup" required>
- </div>
- {{/if}}
- </div>
-
- <div class="modal-foot">
- {{#notNull ruleSuccesses}}
- <a href="#" class="js-modal-close">{{t 'close'}}</a>
- {{else}}
- <button id="restore-profile-submit">{{t 'restore'}}</button>
- <a href="#" class="js-modal-close" id="restore-profile-cancel">{{t 'cancel'}}</a>
- {{/notNull}}
- </div>
-</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js
deleted file mode 100644
index 912aecb10cb..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-// @flow
-import ModalFormView from '../../../components/common/modal-form';
-import Template from '../templates/quality-profiles-change-profile-parent.hbs';
-import { changeProfileParent } from '../../../api/quality-profiles';
-
-export default ModalFormView.extend({
- template: Template,
-
- onRender() {
- ModalFormView.prototype.onRender.apply(this, arguments);
- this.$('select').select2({
- allowClear: false,
- width: '250px',
- minimumResultsForSearch: 50
- });
- },
-
- onFormSubmit() {
- ModalFormView.prototype.onFormSubmit.apply(this, arguments);
- this.disableForm();
- this.sendRequest();
- },
-
- sendRequest() {
- let parent = this.$('#change-profile-parent').val();
- if (parent === '#none') {
- parent = '';
- }
- changeProfileParent(this.options.profile.key, parent)
- .then(() => {
- this.destroy();
- this.trigger('done');
- })
- .catch(e => {
- if (e.response.status === 400) {
- this.enableForm();
- e.response.json().then(r => this.showErrors(r.errors, r.warnings));
- }
- });
- },
-
- serializeData() {
- const { profile } = this.options;
- const profiles = this.options.profiles.filter(
- p => p !== profile && p.language === profile.language
- );
- return { ...profile, profiles };
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js
deleted file mode 100644
index 711946b8bfa..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-// @flow
-import ModalFormView from '../../../components/common/modal-form';
-import Template from '../templates/quality-profiles-copy-profile.hbs';
-import { copyProfile } from '../../../api/quality-profiles';
-
-export default ModalFormView.extend({
- template: Template,
-
- onFormSubmit() {
- ModalFormView.prototype.onFormSubmit.apply(this, arguments);
- this.disableForm();
- this.sendRequest();
- },
-
- sendRequest() {
- const name = this.$('#copy-profile-name').val();
- copyProfile(this.options.profile.key, name)
- .then(profile => {
- this.destroy();
- this.trigger('done', profile);
- })
- .catch(e => {
- if (e.response.status === 400) {
- this.enableForm();
- e.response.json().then(r => this.showErrors(r.errors, r.warnings));
- }
- });
- },
-
- serializeData() {
- return this.options.profile;
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js
deleted file mode 100644
index fb74811ec69..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-// @flow
-import $ from 'jquery';
-import ModalFormView from '../../../components/common/modal-form';
-import Template from '../templates/quality-profiles-create-profile.hbs';
-import { createQualityProfile } from '../../../api/quality-profiles';
-
-export default ModalFormView.extend({
- template: Template,
-
- events() {
- return {
- ...ModalFormView.prototype.events.apply(this, arguments),
- 'change #create-profile-language': 'onLanguageChange'
- };
- },
-
- onFormSubmit() {
- ModalFormView.prototype.onFormSubmit.apply(this, arguments);
-
- const form = this.$('form')[0];
- const data = new FormData(form);
-
- createQualityProfile(data)
- .then(r => {
- this.trigger('done', r.profile);
- this.destroy();
- })
- .catch(e => {
- e.response.json().then(r => this.showErrors(r.errors, r.warnings));
- });
- },
-
- onRender() {
- ModalFormView.prototype.onRender.apply(this, arguments);
- this.$('select').select2({
- width: '250px',
- minimumResultsForSearch: 50
- });
- this.onLanguageChange();
- },
-
- onLanguageChange() {
- const that = this;
- const language = this.$('#create-profile-language').val();
- const importers = this.getImportersForLanguages(language);
- this.$('.js-importer').each(function() {
- that.emptyInput($(this));
- $(this).addClass('hidden');
- });
- importers.forEach(importer => {
- that.$(`.js-importer[data-key="${importer.key}"]`).removeClass('hidden');
- });
- },
-
- emptyInput(e) {
- e.wrap('<form>').closest('form').get(0).reset();
- e.unwrap();
- },
-
- getImportersForLanguages(language) {
- if (language != null) {
- return this.options.importers.filter(importer => importer.languages.indexOf(language) !== -1);
- } else {
- return [];
- }
- },
-
- serializeData() {
- return {
- ...ModalFormView.prototype.serializeData.apply(this, arguments),
- languages: this.options.languages,
- importers: this.options.importers,
- organization: this.options.organization
- };
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js
deleted file mode 100644
index ec4c5f878a4..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-// @flow
-import ModalFormView from '../../../components/common/modal-form';
-import Template from '../templates/quality-profiles-delete-profile.hbs';
-import { deleteProfile } from '../../../api/quality-profiles';
-
-export default ModalFormView.extend({
- template: Template,
-
- modelEvents: {
- destroy: 'destroy'
- },
-
- onFormSubmit() {
- ModalFormView.prototype.onFormSubmit.apply(this, arguments);
- this.disableForm();
- this.sendRequest();
- },
-
- sendRequest() {
- deleteProfile(this.options.profile.key)
- .then(() => {
- this.destroy();
- this.trigger('done');
- })
- .catch(e => {
- if (e.response.status === 400) {
- this.enableForm();
- e.response.json().then(r => this.showErrors(r.errors, r.warnings));
- }
- });
- },
-
- serializeData() {
- return this.options.profile;
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js
deleted file mode 100644
index 57e3e5c5e67..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-// @flow
-import ModalFormView from '../../../components/common/modal-form';
-import Template from '../templates/quality-profiles-rename-profile.hbs';
-import { renameProfile } from '../../../api/quality-profiles';
-
-export default ModalFormView.extend({
- template: Template,
-
- onFormSubmit() {
- ModalFormView.prototype.onFormSubmit.apply(this, arguments);
- this.sendRequest();
- },
-
- sendRequest() {
- const name = this.$('#rename-profile-name').val();
- renameProfile(this.options.profile.key, name)
- .then(() => {
- this.destroy();
- this.trigger('done', name);
- })
- .catch(e => {
- if (e.response.status === 400) {
- this.enableForm();
- e.response.json().then(r => this.showErrors(r.errors, r.warnings));
- }
- });
- },
-
- serializeData() {
- return this.options.profile;
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js
deleted file mode 100644
index d2393e5053d..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-// @flow
-import ModalFormView from '../../../components/common/modal-form';
-import Template from '../templates/quality-profiles-restore-profile.hbs';
-import { restoreQualityProfile } from '../../../api/quality-profiles';
-
-export default ModalFormView.extend({
- template: Template,
-
- onFormSubmit(e) {
- ModalFormView.prototype.onFormSubmit.apply(this, arguments);
- const data = new FormData(e.currentTarget);
-
- this.disableForm();
-
- restoreQualityProfile(data)
- .then(r => {
- this.profile = r.profile;
- this.ruleSuccesses = r.ruleSuccesses;
- this.ruleFailures = r.ruleFailures;
- this.render();
- this.trigger('done');
- })
- .catch(e => {
- this.enableForm();
- e.response.json().then(r => this.showErrors(r.errors, r.warnings));
- });
- },
-
- serializeData() {
- return {
- ...ModalFormView.prototype.serializeData.apply(this, arguments),
- organization: this.options.organization,
- profile: this.profile,
- ruleSuccesses: this.ruleSuccesses,
- ruleFailures: this.ruleFailures
- };
- }
-});