]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7922 Rewrite "Quality Profiles" project page (#1118)
authorStas Vilchik <vilchiks@gmail.com>
Tue, 26 Jul 2016 14:21:19 +0000 (16:21 +0200)
committerGitHub <noreply@github.com>
Tue, 26 Jul 2016 14:21:19 +0000 (16:21 +0200)
16 files changed:
it/it-tests/src/test/resources/authorisation/QualityProfileAdminPermissionTest/profile-admin.html
server/sonar-web/src/main/js/api/quality-profiles.js
server/sonar-web/src/main/js/apps/project-admin/app.js
server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js
server/sonar-web/src/main/js/apps/project-admin/quality-profiles/Header.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/project-admin/quality-profiles/ProfileRow.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/project-admin/quality-profiles/Table.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/project-admin/store/actions.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/project-admin/store/profiles.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/project-admin/store/profilesByProject.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/project-admin/store/rootReducer.js [new file with mode: 0644]
server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
server/sonar-web/src/main/webapp/WEB-INF/app/views/project/quality_profiles.html.erb [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 083c7539e19669520ba5f6cdb72a3a9aee8e4396..9c0efa135d04a82801e174c1408ba5ea2d6b62da 100644 (file)
@@ -65,7 +65,7 @@
 </tr>
 <tr>
   <td>open</td>
-  <td>/project/profile/sample</td>
+  <td>/project/quality_profiles?id=sample</td>
   <td></td>
 </tr>
 <tr>
@@ -74,9 +74,9 @@
   <td>*Quality Profiles*</td>
 </tr>
 <tr>
-  <td>assertValue</td>
-  <td>id=submit-xoo</td>
-  <td>glob:*Update*</td>
+  <td>waitForText</td>
+  <td>id=content</td>
+  <td>*Xoo*</td>
 </tr>
 </tbody>
 </table>
index 4ddb455e9e694dedf1f3388aece2c3aacbffefda..924869837c070bb426f1a2e500251a9d0546c76a 100644 (file)
@@ -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);
+}
index ee3493544d39d581ccc2a5cc4215519ed9da6a2b..7bcb91f9e1c13723abfe1a75836563bcfdbd21ca 100644 (file)
  */
 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);
 });
index 81d52fb3ba231d5652557fc766096613ad1c2d4f..354935418fac0e84f2c7bb13e53d308694b449aa 100644 (file)
  * 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 (file)
index 0000000..f93fb46
--- /dev/null
@@ -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 (file)
index 0000000..50227fa
--- /dev/null
@@ -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 (file)
index 0000000..766e4bf
--- /dev/null
@@ -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 (file)
index 0000000..3e0eaae
--- /dev/null
@@ -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 (file)
index 0000000..cad25a7
--- /dev/null
@@ -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 (file)
index 0000000..d3f93cc
--- /dev/null
@@ -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 (file)
index 0000000..90f8be6
--- /dev/null
@@ -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 (file)
index 0000000..5f5dc3d
--- /dev/null
@@ -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));
index ad6ad6c6e76b1b444d426aa88d19b4a1242535ae..e93564944d6961612c0b2aa5b8822de2fc840317 100644 (file)
@@ -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() {
index ab0d54009634180b0dae66848f841ae56c914b2c..5754eccd08647b04eaa111380af9357f73768208 100644 (file)
@@ -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 (file)
index 0000000..e9dd9ae
--- /dev/null
@@ -0,0 +1,3 @@
+<% content_for :extra_script do %>
+  <script src="<%= ApplicationController.root_context -%>/js/bundles/project-admin.js?v=<%= sonar_version -%>"></script>
+<% end %>
index 94cab9055d4cf93bfe02d0945137ce5a5ef96131..673543d3296cb6dcccaab7a9f9aa53a781c499d4 100644 (file)
@@ -123,6 +123,7 @@ permalinks=Permalinks
 plugin=Plugin
 project=Project
 projects=Projects
+quality_profile=Quality Profile
 raw=Raw
 recent_history=Recent History
 refresh=Refresh