aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/src/main/js/api/quality-profiles.js16
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/app.js19
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js69
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/quality-profiles/Header.js36
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/quality-profiles/ProfileRow.js101
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js75
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/quality-profiles/Table.js71
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/store/actions.js70
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/store/profiles.js39
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/store/profilesByProject.js44
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/store/rootReducer.js42
-rw-r--r--server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js6
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb34
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/project/quality_profiles.html.erb3
14 files changed, 582 insertions, 43 deletions
diff --git a/server/sonar-web/src/main/js/api/quality-profiles.js b/server/sonar-web/src/main/js/api/quality-profiles.js
index 4ddb455e9e6..924869837c0 100644
--- a/server/sonar-web/src/main/js/api/quality-profiles.js
+++ b/server/sonar-web/src/main/js/api/quality-profiles.js
@@ -26,9 +26,9 @@ import {
postJSON
} from '../helpers/request';
-export function getQualityProfiles () {
+export function getQualityProfiles (data) {
const url = '/api/qualityprofiles/search';
- return getJSON(url).then(r => r.profiles);
+ return getJSON(url, data).then(r => r.profiles);
}
export function createQualityProfile (data) {
@@ -163,3 +163,15 @@ export function compareProfiles (leftKey, rightKey) {
const data = { leftKey, rightKey };
return getJSON(url, data);
}
+
+export function associateProject (profileKey, projectKey) {
+ const url = '/api/qualityprofiles/add_project';
+ const data = { profileKey, projectKey };
+ return post(url, data);
+}
+
+export function dissociateProject (profileKey, projectKey) {
+ const url = '/api/qualityprofiles/remove_project';
+ const data = { profileKey, projectKey };
+ return post(url, data);
+}
diff --git a/server/sonar-web/src/main/js/apps/project-admin/app.js b/server/sonar-web/src/main/js/apps/project-admin/app.js
index ee3493544d3..7bcb91f9e1c 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/app.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/app.js
@@ -19,9 +19,13 @@
*/
import React from 'react';
import { render } from 'react-dom';
+import { Provider } from 'react-redux';
import { Router, Route, useRouterHistory } from 'react-router';
import { createHistory } from 'history';
import Deletion from './deletion/Deletion';
+import QualityProfiles from './quality-profiles/QualityProfiles';
+import rootReducer from './store/rootReducer';
+import configureStore from '../../components/store/configureStore';
window.sonarqube.appStarted.then(options => {
const el = document.querySelector(options.el);
@@ -30,12 +34,21 @@ window.sonarqube.appStarted.then(options => {
basename: window.baseUrl + '/project'
});
+ const store = configureStore(rootReducer);
+
const withComponent = ComposedComponent => props =>
<ComposedComponent {...props} component={options.component}/>;
render((
- <Router history={history}>
- <Route path="/deletion" component={withComponent(Deletion)}/>
- </Router>
+ <Provider store={store}>
+ <Router history={history}>
+ <Route
+ path="/deletion"
+ component={withComponent(Deletion)}/>
+ <Route
+ path="/quality_profiles"
+ component={withComponent(QualityProfiles)}/>
+ </Router>
+ </Provider>
), el);
});
diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js b/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js
index 81d52fb3ba2..354935418fa 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js
@@ -18,24 +18,39 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
-import ConfirmationModal from './ConfirmationModal';
-import { translate } from '../../../helpers/l10n';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { deleteProject } from '../../../api/components';
export default class Form extends React.Component {
static propTypes = {
component: React.PropTypes.object.isRequired
};
+ state = {
+ confirmation: false,
+ loading: false
+ };
+
handleDelete (e) {
e.preventDefault();
- new ConfirmationModal({ project: this.props.component })
- .on('done', () => {
- window.location = window.baseUrl + '/';
- })
- .render();
+ this.setState({ confirmation: true });
}
- render () {
+ confirmDeleteClick (e) {
+ e.preventDefault();
+ this.setState({ loading: true });
+ deleteProject(this.props.component.key).then(() => {
+ window.location = window.baseUrl + '/';
+ });
+ }
+
+ cancelDeleteClick (e) {
+ e.preventDefault();
+ e.target.blur();
+ this.setState({ confirmation: false });
+ }
+
+ renderInitial () {
return (
<form onSubmit={this.handleDelete.bind(this)}>
<button id="delete-project" className="button-red">
@@ -44,4 +59,42 @@ export default class Form extends React.Component {
</form>
);
}
+
+ renderConfirmation () {
+ return (
+ <form className="panel panel-warning"
+ onSubmit={this.confirmDeleteClick.bind(this)}>
+ <div className="big-spacer-bottom">
+ {translateWithParameters(
+ 'project_deletion.delete_resource_confirmation',
+ this.props.component.name)}
+ </div>
+
+ <div>
+ <button
+ id="confirm-project-deletion"
+ className="button-red"
+ disabled={this.state.loading}>
+ {translate('delete')}
+ </button>
+
+ {this.state.loading ? (
+ <i className="spinner big-spacer-left"/>
+ ) : (
+ <a href="#"
+ className="big-spacer-left"
+ onClick={this.cancelDeleteClick.bind(this)}>
+ {translate('cancel')}
+ </a>
+ )}
+ </div>
+ </form>
+ );
+ }
+
+ render () {
+ return this.state.confirmation ?
+ this.renderConfirmation() :
+ this.renderInitial();
+ }
}
diff --git a/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/Header.js b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/Header.js
new file mode 100644
index 00000000000..f93fb46e926
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/Header.js
@@ -0,0 +1,36 @@
+/*
+ * 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 { translate } from '../../../helpers/l10n';
+
+export default class Header extends React.Component {
+ render () {
+ return (
+ <header className="page-header">
+ <h1 className="page-title">
+ {translate('project_quality_profiles.page')}
+ </h1>
+ <div className="page-description">
+ {translate('project_quality_profiles.page.description')}
+ </div>
+ </header>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/ProfileRow.js b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/ProfileRow.js
new file mode 100644
index 00000000000..50227fa19a9
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/ProfileRow.js
@@ -0,0 +1,101 @@
+/*
+ * 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 shallowCompare from 'react-addons-shallow-compare';
+import Select from 'react-select';
+import { translate } from '../../../helpers/l10n';
+
+export default class ProfileRow extends React.Component {
+ static propTypes = {
+ profile: React.PropTypes.object.isRequired,
+ possibleProfiles: React.PropTypes.array.isRequired,
+ onChangeProfile: React.PropTypes.func.isRequired
+ };
+
+ state = {
+ loading: false
+ };
+
+ shouldComponentUpdate (nextProps, nextState) {
+ return shallowCompare(this, nextProps, nextState);
+ }
+
+ componentWillUpdate (nextProps) {
+ if (nextProps.profile !== this.props.profile) {
+ this.setState({ loading: false });
+ }
+ }
+
+ handleChange (option) {
+ if (this.props.profile.key !== option.value) {
+ this.setState({ loading: true });
+ this.props.onChangeProfile(this.props.profile.key, option.value);
+ }
+ }
+
+ renderProfileName (profileOption) {
+ if (profileOption.isDefault) {
+ return (
+ <span>
+ <strong>{translate('default')}</strong>
+ {': '}
+ {profileOption.label}
+ </span>
+ );
+ }
+
+ return profileOption.label;
+ }
+
+ renderProfileSelect () {
+ const { profile, possibleProfiles } = this.props;
+
+ const options = possibleProfiles.map(profile => ({
+ value: profile.key,
+ label: profile.name,
+ isDefault: profile.isDefault
+ }));
+
+ return (
+ <Select
+ options={options}
+ valueRenderer={this.renderProfileName}
+ optionRenderer={this.renderProfileName}
+ value={profile.key}
+ clearable={false}
+ style={{ width: 300 }}
+ disabled={this.state.loading}
+ onChange={this.handleChange.bind(this)}/>
+ );
+ }
+
+ render () {
+ const { profile } = this.props;
+
+ return (
+ <tr data-key={profile.language}>
+ <td className="thin nowrap">{profile.languageName}</td>
+ <td className="thin nowrap">{this.renderProfileSelect()}</td>
+ <td>{this.state.loading && <i className="spinner"/>}
+ </td>
+ </tr>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js
new file mode 100644
index 00000000000..766e4bf5dae
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js
@@ -0,0 +1,75 @@
+/*
+ * 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 { connect } from 'react-redux';
+import shallowCompare from 'react-addons-shallow-compare';
+import Header from './Header';
+import Table from './Table';
+import { fetchProjectProfiles, setProjectProfile } from '../store/actions';
+import { getProjectProfiles, getAllProfiles } from '../store/rootReducer';
+
+class QualityProfiles extends React.Component {
+ static propTypes = {
+ component: React.PropTypes.object.isRequired,
+ allProfiles: React.PropTypes.array,
+ profiles: React.PropTypes.array
+ };
+
+ componentDidMount () {
+ this.props.fetchProjectProfiles(this.props.component.key);
+ }
+
+ shouldComponentUpdate (nextProps, nextState) {
+ return shallowCompare(this, nextProps, nextState);
+ }
+
+ handleChangeProfile (oldKey, newKey) {
+ this.props.setProjectProfile(this.props.component.key, oldKey, newKey);
+ }
+
+ render () {
+ const { allProfiles, profiles } = this.props;
+
+ return (
+ <div className="page page-limited">
+ <Header/>
+
+ {profiles.length > 0 ? (
+ <Table
+ allProfiles={allProfiles}
+ profiles={profiles}
+ onChangeProfile={this.handleChangeProfile.bind(this)}/>
+ ) : (
+ <i className="spinner"/>
+ )}
+ </div>
+ );
+ }
+}
+
+const mapStateToProps = (state, ownProps) => ({
+ allProfiles: getAllProfiles(state),
+ profiles: getProjectProfiles(state, ownProps.component.key)
+});
+
+export default connect(
+ mapStateToProps,
+ { fetchProjectProfiles, setProjectProfile }
+)(QualityProfiles);
diff --git a/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/Table.js b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/Table.js
new file mode 100644
index 00000000000..3e0eaae5b32
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/Table.js
@@ -0,0 +1,71 @@
+/*
+ * 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 shallowCompare from 'react-addons-shallow-compare';
+import groupBy from 'lodash/groupBy';
+import orderBy from 'lodash/orderBy';
+import ProfileRow from './ProfileRow';
+import { translate } from '../../../helpers/l10n';
+
+export default class Table extends React.Component {
+ static propTypes = {
+ allProfiles: React.PropTypes.array.isRequired,
+ profiles: React.PropTypes.array.isRequired,
+ onChangeProfile: React.PropTypes.func.isRequired
+ };
+
+ shouldComponentUpdate (nextProps, nextState) {
+ return shallowCompare(this, nextProps, nextState);
+ }
+
+ renderHeader () {
+ // keep one empty cell for the spinner
+ return (
+ <thead>
+ <tr>
+ <th className="thin nowrap">{translate('language')}</th>
+ <th className="thin nowrap">{translate('quality_profile')}</th>
+ <th>&nbsp;</th>
+ </tr>
+ </thead>
+ );
+ }
+
+ render () {
+ const profilesByLanguage = groupBy(this.props.allProfiles, 'language');
+ const orderedProfiles = orderBy(this.props.profiles, 'languageName');
+
+ // set key to language to avoid destroying of component
+ const profileRows = orderedProfiles.map(profile => (
+ <ProfileRow
+ key={profile.language}
+ profile={profile}
+ possibleProfiles={profilesByLanguage[profile.language]}
+ onChangeProfile={this.props.onChangeProfile}/>
+ ));
+
+ return (
+ <table className="data zebra">
+ {this.renderHeader()}
+ <tbody>{profileRows}</tbody>
+ </table>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/project-admin/store/actions.js b/server/sonar-web/src/main/js/apps/project-admin/store/actions.js
new file mode 100644
index 00000000000..cad25a70e7a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/project-admin/store/actions.js
@@ -0,0 +1,70 @@
+/*
+ * 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 {
+ getQualityProfiles,
+ associateProject,
+ dissociateProject
+} from '../../../api/quality-profiles';
+import { getProfileByKey } from './rootReducer';
+
+export const RECEIVE_PROFILES = 'RECEIVE_PROFILES';
+export const receiveProfiles = profiles => ({
+ type: RECEIVE_PROFILES,
+ profiles
+});
+
+export const RECEIVE_PROJECT_PROFILES = 'RECEIVE_PROJECT_PROFILES';
+export const receiveProjectProfiles = (projectKey, profiles) => ({
+ type: RECEIVE_PROJECT_PROFILES,
+ projectKey,
+ profiles
+});
+
+export const fetchProjectProfiles = projectKey => dispatch => {
+ Promise.all([
+ getQualityProfiles(),
+ getQualityProfiles({ projectKey })
+ ]).then(responses => {
+ const [allProfiles, projectProfiles] = responses;
+ dispatch(receiveProfiles(allProfiles));
+ dispatch(receiveProjectProfiles(projectKey, projectProfiles));
+ });
+};
+
+export const SET_PROJECT_PROFILE = 'SET_PROJECT_PROFILE';
+const setProjectProfileAction = (projectKey, oldProfileKey, newProfileKey) => ({
+ type: SET_PROJECT_PROFILE,
+ projectKey,
+ oldProfileKey,
+ newProfileKey
+});
+
+export const setProjectProfile = (projectKey, oldKey, newKey) =>
+ (dispatch, getState) => {
+ const state = getState();
+ const newProfile = getProfileByKey(state, newKey);
+ const request = newProfile.isDefault ?
+ dissociateProject(oldKey, projectKey) :
+ associateProject(newKey, projectKey);
+
+ request.then(() => {
+ dispatch(setProjectProfileAction(projectKey, oldKey, newKey));
+ });
+ };
diff --git a/server/sonar-web/src/main/js/apps/project-admin/store/profiles.js b/server/sonar-web/src/main/js/apps/project-admin/store/profiles.js
new file mode 100644
index 00000000000..d3f93cc257a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/project-admin/store/profiles.js
@@ -0,0 +1,39 @@
+/*
+ * 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 keyBy from 'lodash/keyBy';
+import values from 'lodash/values';
+import { RECEIVE_PROFILES } from './actions';
+
+const profiles = (state = {}, action = {}) => {
+ if (action.type === RECEIVE_PROFILES) {
+ const newProfilesByKey = keyBy(action.profiles, 'key');
+ return { ...state, ...newProfilesByKey };
+ }
+
+ return state;
+};
+
+export default profiles;
+
+export const getAllProfiles = state =>
+ values(state);
+
+export const getProfile = (state, key) =>
+ state[key];
diff --git a/server/sonar-web/src/main/js/apps/project-admin/store/profilesByProject.js b/server/sonar-web/src/main/js/apps/project-admin/store/profilesByProject.js
new file mode 100644
index 00000000000..90f8be6c2cd
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/project-admin/store/profilesByProject.js
@@ -0,0 +1,44 @@
+/*
+ * 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 without from 'lodash/without';
+import { RECEIVE_PROJECT_PROFILES, SET_PROJECT_PROFILE } from './actions';
+
+const profilesByProject = (state = {}, action = {}) => {
+ if (action.type === RECEIVE_PROJECT_PROFILES) {
+ const profileKeys = action.profiles.map(profile => profile.key);
+ return { ...state, [action.projectKey]: profileKeys };
+ }
+
+ if (action.type === SET_PROJECT_PROFILE) {
+ const profileKeys = state[action.projectKey];
+ const nextProfileKeys = [
+ ...without(profileKeys, action.oldProfileKey),
+ action.newProfileKey
+ ];
+ return { ...state, [action.projectKey]: nextProfileKeys };
+ }
+
+ return state;
+};
+
+export default profilesByProject;
+
+export const getProfiles = (state, projectKey) =>
+ state[projectKey] || [];
diff --git a/server/sonar-web/src/main/js/apps/project-admin/store/rootReducer.js b/server/sonar-web/src/main/js/apps/project-admin/store/rootReducer.js
new file mode 100644
index 00000000000..5f5dc3d7899
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/project-admin/store/rootReducer.js
@@ -0,0 +1,42 @@
+/*
+ * 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 { combineReducers } from 'redux';
+import profiles, {
+ getProfile,
+ getAllProfiles as nextGetAllProfiles
+} from './profiles';
+import profilesByProject, { getProfiles } from './profilesByProject';
+
+const rootReducer = combineReducers({
+ profiles,
+ profilesByProject
+});
+
+export default rootReducer;
+
+export const getProfileByKey = (state, profileKey) =>
+ getProfile(state.profiles, profileKey);
+
+export const getAllProfiles = state =>
+ nextGetAllProfiles(state.profiles);
+
+export const getProjectProfiles = (state, projectKey) =>
+ getProfiles(state.profilesByProject, projectKey)
+ .map(profileKey => getProfileByKey(state, profileKey));
diff --git a/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js b/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js
index ad6ad6c6e76..e93564944d6 100644
--- a/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js
+++ b/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js
@@ -27,7 +27,7 @@ import { getComponentUrl } from '../../../helpers/urls';
const SETTINGS_URLS = [
'/project/settings',
- '/project/profile',
+ '/project/quality_profiles',
'/project/qualitygate',
'/custom_measures',
'/project/links',
@@ -149,8 +149,8 @@ export default React.createClass({
if (!this.props.conf.showQualityProfiles) {
return null;
}
- const url = `/project/profile?id=${encodeURIComponent(this.props.component.key)}`;
- return this.renderLink(url, translate('project_quality_profiles.page'), '/project/profile');
+ const url = `/project/quality_profiles?id=${encodeURIComponent(this.props.component.key)}`;
+ return this.renderLink(url, translate('project_quality_profiles.page'), '/project/quality_profiles');
},
renderQualityGatesLink() {
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
index ab0d5400963..5754eccd086 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
@@ -32,36 +32,16 @@ class ProjectController < ApplicationController
@project = get_current_project(params[:id])
end
- # GET /project/profile?id=<project id>
- def profile
- require_parameters :id
- @project_id = Api::Utils.project_id(params[:id])
- @project = Project.by_key(@project_id)
+ def quality_profiles
+ # since 6.1
+ @project = Project.by_key(params[:id])
+ not_found("Project not found") unless @project
access_denied unless (is_admin?(@project.uuid) || has_role?(:profileadmin))
-
- call_backend do
- @all_quality_profiles = Internal.quality_profiles.allProfiles().to_a
- end
end
- # POST /project/set_profile?id=<project id>&language=<language>[&profile_id=<profile id>]
- def set_profile
- verify_post_request
-
- language = params[:language]
- project = get_current_project(params[:id])
- profile_id = params[:profile_id]
-
- call_backend do
- if profile_id.blank?
- Internal.quality_profiles.removeProjectByLanguage(language, project.id())
- else
- profile = Internal.quality_profiles.profile(profile_id.to_i)
- Internal.quality_profiles.addProject(profile.key(), project.uuid())
- end
- end
-
- redirect_to :action => 'profile', :id => project
+ def profile
+ # redirect to another url since 6.1
+ redirect_to(url_for({:action => 'quality_profiles'}) + '?id=' + url_encode(params[:id]))
end
# GET /project/qualitygate?id=<project id>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/quality_profiles.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/quality_profiles.html.erb
new file mode 100644
index 00000000000..e9dd9ae3410
--- /dev/null
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/quality_profiles.html.erb
@@ -0,0 +1,3 @@
+<% content_for :extra_script do %>
+ <script src="<%= ApplicationController.root_context -%>/js/bundles/project-admin.js?v=<%= sonar_version -%>"></script>
+<% end %>