diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2016-11-21 10:03:40 +0100 |
---|---|---|
committer | Stas Vilchik <vilchiks@gmail.com> | 2016-11-28 13:39:04 +0100 |
commit | 3a34147be1e725f71b8e102ba53b1f5b9bd10b13 (patch) | |
tree | 16ab51c591f124cf0e34a7005361392493a6ebc5 /server/sonar-web/src/main/js/apps | |
parent | bd96be1c6c971381952dff396e0e10ae9233a6b7 (diff) | |
download | sonarqube-3a34147be1e725f71b8e102ba53b1f5b9bd10b13.tar.gz sonarqube-3a34147be1e725f71b8e102ba53b1f5b9bd10b13.zip |
step towards a single-entry-point js app (#1362)
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
122 files changed, 1557 insertions, 981 deletions
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js b/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js index 5beee99c9af..0fa65014b61 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js @@ -21,7 +21,6 @@ import React from 'react'; import shallowCompare from 'react-addons-shallow-compare'; import debounce from 'lodash/debounce'; -import { PropTypes as RouterPropTypes } from 'react-router'; import { DEFAULT_FILTERS, DEBOUNCE_DELAY, STATUSES, CURRENTS } from './../constants'; import Header from './Header'; @@ -41,7 +40,7 @@ export default class BackgroundTasksApp extends React.Component { static propTypes = { component: React.PropTypes.object, - location: RouterPropTypes.location.isRequired + location: React.PropTypes.object }; state: any = { @@ -136,7 +135,7 @@ export default class BackgroundTasksApp extends React.Component { }); this.context.router.push({ - pathname: '/', + pathname: this.props.location.pathname, query: nextQuery }); } diff --git a/server/sonar-web/src/main/js/apps/background-tasks/routes.js b/server/sonar-web/src/main/js/apps/background-tasks/routes.js new file mode 100644 index 00000000000..5bbcd616795 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/routes.js @@ -0,0 +1,26 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import BackgroundTasksApp from './components/BackgroundTasksApp'; + +export default ( + <IndexRoute component={BackgroundTasksApp}/> +); diff --git a/server/sonar-web/src/main/js/apps/code/app.js b/server/sonar-web/src/main/js/apps/code/app.js deleted file mode 100644 index fc13bb30a31..00000000000 --- a/server/sonar-web/src/main/js/apps/code/app.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 { render } from 'react-dom'; -import { Router, Route, Redirect, useRouterHistory } from 'react-router'; -import { createHistory } from 'history'; - -import App from './components/App'; - -window.sonarqube.appStarted.then(options => { - const el = document.querySelector(options.el); - - const history = useRouterHistory(createHistory)({ - basename: window.baseUrl + '/code' - }); - - const AppWithComponent = (props) => { - return <App {...props} component={options.component}/>; - }; - - render(( - <Router history={history}> - <Redirect from="/index" to="/"/> - <Route path="/" component={AppWithComponent}/> - </Router> - ), el); -}); diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentName.js b/server/sonar-web/src/main/js/apps/code/components/ComponentName.js index cfe56f47381..ae166bbaa02 100644 --- a/server/sonar-web/src/main/js/apps/code/components/ComponentName.js +++ b/server/sonar-web/src/main/js/apps/code/components/ComponentName.js @@ -48,7 +48,7 @@ function mostCommitPrefix (strings) { return prefix.substr(0, prefix.length - lastPrefixPart.length); } -const Component = ({ component, rootComponent, previous, canBrowse }) => { +const ComponentName = ({ component, rootComponent, previous, canBrowse }) => { const areBothDirs = component.qualifier === 'DIR' && previous && previous.qualifier === 'DIR'; const prefix = areBothDirs ? mostCommitPrefix([component.name + '/', previous.name + '/']) : ''; const name = prefix ? ( @@ -75,7 +75,7 @@ const Component = ({ component, rootComponent, previous, canBrowse }) => { Object.assign(query, { selected: component.key }); } inner = ( - <Link to={{ pathname: '/', query }} className="link-with-icon"> + <Link to={{ pathname: '/code', query }} className="link-with-icon"> <QualifierIcon qualifier={component.qualifier}/> {' '} <span>{name}</span> @@ -99,4 +99,4 @@ const Component = ({ component, rootComponent, previous, canBrowse }) => { ); }; -export default Component; +export default ComponentName; diff --git a/server/sonar-web/src/main/js/apps/code/components/Search.js b/server/sonar-web/src/main/js/apps/code/components/Search.js index d54f727e5f3..659b397074c 100644 --- a/server/sonar-web/src/main/js/apps/code/components/Search.js +++ b/server/sonar-web/src/main/js/apps/code/components/Search.js @@ -103,7 +103,7 @@ export default class Search extends React.Component { window.location = getComponentUrl(selected.refKey); } else { this.context.router.push({ - pathname: '/', + pathname: '/code', query: { id: component.key, selected: selected.key diff --git a/server/sonar-web/src/main/js/apps/markdown/markdown-help-view.js b/server/sonar-web/src/main/js/apps/code/routes.js index 95235579e99..882c1b76882 100644 --- a/server/sonar-web/src/main/js/apps/markdown/markdown-help-view.js +++ b/server/sonar-web/src/main/js/apps/code/routes.js @@ -17,10 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import Marionette from 'backbone.marionette'; -import Template from './templates/markdown-help.hbs'; - -export default Marionette.ItemView.extend({ - template: Template -}); +import React from 'react'; +import { IndexRoute } from 'react-router'; +import App from './components/App'; +export default ( + <IndexRoute component={App}/> +); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/app.js b/server/sonar-web/src/main/js/apps/coding-rules/app.js deleted file mode 100644 index ea4adc1cf3f..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/app.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 $ from 'jquery'; -import _ from 'underscore'; -import Backbone from 'backbone'; -import Marionette from 'backbone.marionette'; -import State from './models/state'; -import Layout from './layout'; -import Rules from './models/rules'; -import Facets from '../../components/navigator/models/facets'; -import Controller from './controller'; -import Router from '../../components/navigator/router'; -import WorkspaceListView from './workspace-list-view'; -import WorkspaceHeaderView from './workspace-header-view'; -import FacetsView from './facets-view'; -import FiltersView from './filters-view'; - -const App = new Marionette.Application(); -const init = function () { - const options = window.sonarqube; - - this.layout = new Layout({ el: options.el }); - this.layout.render(); - $('#footer').addClass('search-navigator-footer'); - - this.state = new State(); - this.list = new Rules(); - this.facets = new Facets(); - - this.controller = new Controller({ app: this }); - - this.workspaceListView = new WorkspaceListView({ - app: this, - collection: this.list - }); - this.layout.workspaceListRegion.show(this.workspaceListView); - this.workspaceListView.bindScrollEvents(); - - this.workspaceHeaderView = new WorkspaceHeaderView({ - app: this, - collection: this.list - }); - this.layout.workspaceHeaderRegion.show(this.workspaceHeaderView); - - this.facetsView = new FacetsView({ - app: this, - collection: this.facets - }); - this.layout.facetsRegion.show(this.facetsView); - - this.filtersView = new FiltersView({ - app: this - }); - this.layout.filtersRegion.show(this.filtersView); - - key.setScope('list'); - this.router = new Router({ - app: this - }); - Backbone.history.start(); -}; - -const appXHR = $.get(window.baseUrl + '/api/rules/app').done(function (r) { - App.canWrite = r.canWrite; - App.qualityProfiles = _.sortBy(r.qualityprofiles, ['name', 'lang']); - App.languages = _.extend(r.languages, { - none: 'None' - }); - _.map(App.qualityProfiles, function (profile) { - profile.language = App.languages[profile.lang]; - }); - App.repositories = r.repositories; - App.statuses = r.statuses; -}); - -App.on('start', function (options) { - appXHR.done(function () { - init.call(App, options); - }); -}); - -window.sonarqube.appStarted.then(options => App.start(options)); diff --git a/server/sonar-web/src/main/js/apps/overview/app.js b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js index 5d8ad1b4a17..97491378c7f 100644 --- a/server/sonar-web/src/main/js/apps/overview/app.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js @@ -18,14 +18,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import { render } from 'react-dom'; +import init from '../init'; -import App from './components/App'; +export default class CodingRulesAppContainer extends React.Component { + componentDidMount () { + init(this.refs.container); + } -window.sonarqube.appStarted.then(options => { - const el = document.querySelector(options.el); - const component = { ...options.component, ...window.sonarqube.overview.component }; - render(( - <App component={component}/> - ), el); -}); + render () { + return <div ref="container"/>; + } +} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/init.js b/server/sonar-web/src/main/js/apps/coding-rules/init.js new file mode 100644 index 00000000000..aa7c9644ede --- /dev/null +++ b/server/sonar-web/src/main/js/apps/coding-rules/init.js @@ -0,0 +1,94 @@ +/* + * 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 $ from 'jquery'; +import _ from 'underscore'; +import Backbone from 'backbone'; +import Marionette from 'backbone.marionette'; +import State from './models/state'; +import Layout from './layout'; +import Rules from './models/rules'; +import Facets from '../../components/navigator/models/facets'; +import Controller from './controller'; +import Router from '../../components/navigator/router'; +import WorkspaceListView from './workspace-list-view'; +import WorkspaceHeaderView from './workspace-header-view'; +import FacetsView from './facets-view'; +import FiltersView from './filters-view'; + +const App = new Marionette.Application(); + +App.on('start', function (el) { + $.get(window.baseUrl + '/api/rules/app').done(function (r) { + App.canWrite = r.canWrite; + App.qualityProfiles = _.sortBy(r.qualityprofiles, ['name', 'lang']); + App.languages = _.extend(r.languages, { + none: 'None' + }); + _.map(App.qualityProfiles, function (profile) { + profile.language = App.languages[profile.lang]; + }); + App.repositories = r.repositories; + App.statuses = r.statuses; + }).done(() => { + this.layout = new Layout({ el }); + this.layout.render(); + $('#footer').addClass('search-navigator-footer'); + + this.state = new State(); + this.list = new Rules(); + this.facets = new Facets(); + + this.controller = new Controller({ app: this }); + + this.workspaceListView = new WorkspaceListView({ + app: this, + collection: this.list + }); + this.layout.workspaceListRegion.show(this.workspaceListView); + this.workspaceListView.bindScrollEvents(); + + this.workspaceHeaderView = new WorkspaceHeaderView({ + app: this, + collection: this.list + }); + this.layout.workspaceHeaderRegion.show(this.workspaceHeaderView); + + this.facetsView = new FacetsView({ + app: this, + collection: this.facets + }); + this.layout.facetsRegion.show(this.facetsView); + + this.filtersView = new FiltersView({ + app: this + }); + this.layout.filtersRegion.show(this.filtersView); + + key.setScope('list'); + this.router = new Router({ + app: this + }); + Backbone.history.start(); + }); +}); + +export default function (el) { + App.start(el); +} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/routes.js b/server/sonar-web/src/main/js/apps/coding-rules/routes.js new file mode 100644 index 00000000000..9688c9e7819 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/coding-rules/routes.js @@ -0,0 +1,26 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import CodingRulesAppContainer from './components/CodingRulesAppContainer'; + +export default ( + <IndexRoute component={CodingRulesAppContainer}/> +); diff --git a/server/sonar-web/src/main/js/apps/component-issues/components/ComponentIssuesAppContainer.js b/server/sonar-web/src/main/js/apps/component-issues/components/ComponentIssuesAppContainer.js new file mode 100644 index 00000000000..430ba7b0e79 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-issues/components/ComponentIssuesAppContainer.js @@ -0,0 +1,31 @@ +/* + * 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 init from '../init'; + +export default class ComponentIssuesAppContainer extends React.Component { + componentDidMount () { + init(this.refs.container); + } + + render () { + return <div ref="container"/>; + } +} diff --git a/server/sonar-web/src/main/js/apps/component-issues/app.js b/server/sonar-web/src/main/js/apps/component-issues/init.js index 7397e84eed9..95269406d71 100644 --- a/server/sonar-web/src/main/js/apps/component-issues/app.js +++ b/server/sonar-web/src/main/js/apps/component-issues/init.js @@ -33,7 +33,7 @@ import FacetsView from './../issues/facets-view'; import HeaderView from './../issues/HeaderView'; const App = new Marionette.Application(); -const init = function () { +const init = function (el) { const options = window.sonarqube; this.config = options.config; @@ -49,7 +49,7 @@ const init = function () { this.list = new Issues(); this.facets = new Facets(); - this.layout = new Layout({ app: this, el: options.el }); + this.layout = new Layout({ app: this, el }); this.layout.render(); $('#footer').addClass('search-navigator-footer'); @@ -109,8 +109,10 @@ App.updateContextFacets = function () { }); }; -App.on('start', function (options) { - init.call(App, options); +App.on('start', function (el) { + init.call(App, el); }); -window.sonarqube.appStarted.then(options => App.start(options)); +export default function (el) { + App.start(el); +} diff --git a/server/sonar-web/src/main/js/apps/component-issues/routes.js b/server/sonar-web/src/main/js/apps/component-issues/routes.js new file mode 100644 index 00000000000..e07322509f2 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-issues/routes.js @@ -0,0 +1,26 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import ComponentIssuesAppContainer from './components/ComponentIssuesAppContainer'; + +export default ( + <IndexRoute component={ComponentIssuesAppContainer}/> +); diff --git a/server/sonar-web/src/main/js/apps/component-measures/app.js b/server/sonar-web/src/main/js/apps/component-measures/app.js deleted file mode 100644 index 230a10f092a..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/app.js +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 { render } from 'react-dom'; -import { Router, Route, IndexRoute, Redirect, IndexRedirect, useRouterHistory } from 'react-router'; -import { createHistory } from 'history'; -import { Provider } from 'react-redux'; - -import AppContainer from './app/AppContainer'; -import HomeContainer from './home/HomeContainer'; -import AllMeasuresContainer from './home/AllMeasuresContainer'; -import DomainMeasuresContainer from './home/DomainMeasuresContainer'; -import MeasureDetailsContainer from './details/MeasureDetailsContainer'; -import ListViewContainer from './details/drilldown/ListViewContainer'; -import TreeViewContainer from './details/drilldown/TreeViewContainer'; -import MeasureHistoryContainer from './details/history/MeasureHistoryContainer'; -import MeasureTreemapContainer from './details/treemap/MeasureTreemapContainer'; - -import configureStore from './store/configureStore'; - -import { checkHistoryExistence } from './hooks'; - -import './styles.css'; - -window.sonarqube.appStarted.then(options => { - const el = document.querySelector(options.el); - - const history = useRouterHistory(createHistory)({ - basename: window.baseUrl + '/component_measures' - }); - - const store = configureStore({ - app: { component: options.component } - }); - - const handleRouteUpdate = () => { - window.scrollTo(0, 0); - }; - - render(( - <Provider store={store}> - <Router history={history} onUpdate={handleRouteUpdate}> - <Redirect from="/index" to="/"/> - - <Route path="/" component={AppContainer}> - <Route component={HomeContainer}> - <IndexRoute component={AllMeasuresContainer}/> - <Route path="domain/:domainName" component={DomainMeasuresContainer}/> - </Route> - - <Route path="metric/:metricKey" component={MeasureDetailsContainer}> - <IndexRedirect to="list"/> - <Route path="list" component={ListViewContainer}/> - <Route path="tree" component={TreeViewContainer}/> - <Route path="history" component={MeasureHistoryContainer} onEnter={checkHistoryExistence}/> - <Route path="treemap" component={MeasureTreemapContainer}/> - </Route> - </Route> - - <Redirect from="*" to="/"/> - </Router> - </Provider> - ), el); -}); diff --git a/server/sonar-web/src/main/js/apps/component-measures/app/App.js b/server/sonar-web/src/main/js/apps/component-measures/app/App.js index fa23544cd91..75fa2d320a4 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/app/App.js +++ b/server/sonar-web/src/main/js/apps/component-measures/app/App.js @@ -23,6 +23,7 @@ import Spinner from './../components/Spinner'; export default class App extends React.Component { componentDidMount () { + this.props.setComponent(this.props.component); this.props.fetchMetrics(); } diff --git a/server/sonar-web/src/main/js/apps/component-measures/app/AppContainer.js b/server/sonar-web/src/main/js/apps/component-measures/app/AppContainer.js index 2bd53a0ab19..c3e9e632d10 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/app/AppContainer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/app/AppContainer.js @@ -18,19 +18,20 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { connect } from 'react-redux'; - import App from './App'; -import { fetchMetrics } from './actions'; +import { fetchMetrics, setComponent } from './actions'; +import { getMeasuresAppAllMetrics } from '../../../app/store/rootReducer'; const mapStateToProps = state => { return { - metrics: state.app.metrics + metrics: getMeasuresAppAllMetrics(state) }; }; const mapDispatchToProps = dispatch => { return { - fetchMetrics: () => dispatch(fetchMetrics()) + fetchMetrics: () => dispatch(fetchMetrics()), + setComponent: component => dispatch(setComponent(component)) }; }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/app/actions.js b/server/sonar-web/src/main/js/apps/component-measures/app/actions.js index 85f74ffa9a4..92e80e68fc1 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/app/actions.js +++ b/server/sonar-web/src/main/js/apps/component-measures/app/actions.js @@ -23,8 +23,9 @@ import { getMetrics } from '../../../api/metrics'; * Actions */ -export const DISPLAY_HOME = 'app/DISPLAY_HOME'; -export const RECEIVE_METRICS = 'app/RECEIVE_METRICS'; +export const DISPLAY_HOME = 'measuresApp/app/DISPLAY_HOME'; +export const RECEIVE_METRICS = 'measuresApp/app/RECEIVE_METRICS'; +export const SET_COMPONENT = 'measuresApp/app/SET_COMPONENT'; /* * Action Creators @@ -38,6 +39,10 @@ function receiveMetrics (metrics) { return { type: RECEIVE_METRICS, metrics }; } +export function setComponent (component) { + return { type: SET_COMPONENT, component }; +} + /* * Workflow */ diff --git a/server/sonar-web/src/main/js/apps/component-measures/app/reducer.js b/server/sonar-web/src/main/js/apps/component-measures/app/reducer.js index 2484e2d0563..0df67b1d78f 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/app/reducer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/app/reducer.js @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { RECEIVE_METRICS } from './actions'; +import { RECEIVE_METRICS, SET_COMPONENT } from './actions'; const initialState = { metrics: undefined @@ -27,6 +27,8 @@ export default function appReducer (state = initialState, action = {}) { switch (action.type) { case RECEIVE_METRICS: return { ...state, metrics: action.metrics }; + case SET_COMPONENT: + return { ...state, component: action.component }; default: return state; } diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/bubbleChart/MeasureBubbleChartContainer.js b/server/sonar-web/src/main/js/apps/component-measures/components/bubbleChart/MeasureBubbleChartContainer.js index f219bfe01cc..029cee95b55 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/bubbleChart/MeasureBubbleChartContainer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/bubbleChart/MeasureBubbleChartContainer.js @@ -18,13 +18,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { connect } from 'react-redux'; - import MeasureBubbleChart from './BubbleChart'; +import { getMeasuresAppAllMetrics, getMeasuresAppComponent } from '../../../../app/store/rootReducer'; const mapStateToProps = state => { return { - component: state.app.component, - metrics: state.app.metrics + component: getMeasuresAppComponent(state), + metrics: getMeasuresAppAllMetrics(state) }; }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/details/MeasureDetails.js b/server/sonar-web/src/main/js/apps/component-measures/details/MeasureDetails.js index 35d400d3e4c..125b9c72897 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/details/MeasureDetails.js +++ b/server/sonar-web/src/main/js/apps/component-measures/details/MeasureDetails.js @@ -71,7 +71,7 @@ export default class MeasureDetails extends React.Component { <section id="component-measures-details" className="page page-container page-limited"> <div className="note"> <IndexLink - to={{ pathname: '/', query: { id: component.key } }} + to={{ pathname: '/component_measures', query: { id: component.key } }} id="component-measures-back-to-all-measures" className="text-muted"> {translate('component_measures.all_measures')} @@ -80,7 +80,7 @@ export default class MeasureDetails extends React.Component { <span> {' / '} <Link - to={{ pathname: `domain/${metric.domain}`, query: { id: component.key } }} + to={{ pathname: `/component_measures/domain/${metric.domain}`, query: { id: component.key } }} className="text-muted"> {translateWithParameters('component_measures.domain_measures', metric.domain)} </Link> diff --git a/server/sonar-web/src/main/js/apps/component-measures/details/MeasureDetailsContainer.js b/server/sonar-web/src/main/js/apps/component-measures/details/MeasureDetailsContainer.js index b0d87963394..055ce5225e3 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/details/MeasureDetailsContainer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/details/MeasureDetailsContainer.js @@ -18,18 +18,25 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { connect } from 'react-redux'; - import MeasureDetails from './MeasureDetails'; import { fetchMeasure } from './actions'; +import { + getMeasuresAppAllMetrics, + getMeasuresAppDetailsMetric, + getMeasuresAppDetailsMeasure, + getMeasuresAppDetailsSecondaryMeasure, + getMeasuresAppDetailsPeriods + , getMeasuresAppComponent +} from '../../../app/store/rootReducer'; const mapStateToProps = state => { return { - component: state.app.component, - metrics: state.app.metrics, - metric: state.details.metric, - measure: state.details.measure, - secondaryMeasure: state.details.secondaryMeasure, - periods: state.details.periods + component: getMeasuresAppComponent(state), + metrics: getMeasuresAppAllMetrics(state), + metric: getMeasuresAppDetailsMetric(state), + measure: getMeasuresAppDetailsMeasure(state), + secondaryMeasure: getMeasuresAppDetailsSecondaryMeasure(state), + periods: getMeasuresAppDetailsPeriods(state) }; }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/details/actions.js b/server/sonar-web/src/main/js/apps/component-measures/details/actions.js index 7f24fce6112..608c7e2b97c 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/details/actions.js +++ b/server/sonar-web/src/main/js/apps/component-measures/details/actions.js @@ -19,13 +19,14 @@ */ import { getMeasuresAndMeta } from '../../../api/measures'; import { enhanceWithLeak } from '../utils'; +import { getMeasuresAppComponent, getMeasuresAppAllMetrics } from '../../../app/store/rootReducer'; /* * Actions */ -export const REQUEST_MEASURE = 'details/REQUEST_MEASURE'; -export const RECEIVE_MEASURE = 'details/RECEIVE_MEASURE'; +export const REQUEST_MEASURE = 'measuresApp/details/REQUEST_MEASURE'; +export const RECEIVE_MEASURE = 'measuresApp/details/RECEIVE_MEASURE'; /* * Action Creators @@ -45,7 +46,9 @@ function receiveMeasure (measure, secondaryMeasure, periods) { export function fetchMeasure (metricKey, periodIndex = 1) { return (dispatch, getState) => { - const { component, metrics } = getState().app; + const state = getState(); + const component = getMeasuresAppComponent(state); + const metrics = getMeasuresAppAllMetrics(state); const metricsToRequest = [metricKey]; if (metricKey === 'ncloc') { diff --git a/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/ListViewContainer.js b/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/ListViewContainer.js index c96c5586383..d982d7aef88 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/ListViewContainer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/ListViewContainer.js @@ -18,24 +18,29 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { connect } from 'react-redux'; -import pick from '../../../../../../../node_modules/lodash/pick'; - import ListView from './ListView'; import { fetchList, fetchMore, selectComponent, selectNext, selectPrevious } from '../../store/listViewActions'; +import { + getMeasuresAppListComponents, + getMeasuresAppListSelected, + getMeasuresAppListTotal, + getMeasuresAppListPageIndex, + getMeasuresAppAllMetrics, + getMeasuresAppDetailsMetric, + isMeasuresAppFetching + , getMeasuresAppComponent +} from '../../../../app/store/rootReducer'; const mapStateToProps = state => { - const drilldown = pick(state.list, [ - 'components', - 'selected', - 'total', - 'pageIndex' - ]); return { - ...drilldown, - component: state.app.component, - metrics: state.app.metrics, - metric: state.details.metric, - fetching: state.status.fetching + components: getMeasuresAppListComponents(state), + selected: getMeasuresAppListSelected(state), + total: getMeasuresAppListTotal(state), + pageIndex: getMeasuresAppListPageIndex(state), + component: getMeasuresAppComponent(state), + metrics: getMeasuresAppAllMetrics(state), + metric: getMeasuresAppDetailsMetric(state), + fetching: isMeasuresAppFetching(state) }; }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/MeasureDrilldown.js b/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/MeasureDrilldown.js index f41d19f8411..cc7e06546ed 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/MeasureDrilldown.js +++ b/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/MeasureDrilldown.js @@ -39,20 +39,20 @@ export default class MeasureDrilldown extends React.Component { <div className="measure-details-drilldown"> <ul className="measure-details-drilldown-mode"> {component.qualifier !== 'DEV' && ( - <li> - <Link - activeClassName="active" - to={{ pathname: `metric/${metric.key}/list`, query: { id: component.key } }}> - <IconList/> - {translate('component_measures.tab.list')} - </Link> - </li> + <li> + <Link + activeClassName="active" + to={{ pathname: `/component_measures/metric/${metric.key}/list`, query: { id: component.key } }}> + <IconList/> + {translate('component_measures.tab.list')} + </Link> + </li> )} <li> <Link activeClassName="active" - to={{ pathname: `metric/${metric.key}/tree`, query: { id: component.key } }}> + to={{ pathname: `/component_measures/metric/${metric.key}/tree`, query: { id: component.key } }}> <IconTree/> {translate('component_measures.tab.tree')} </Link> @@ -62,7 +62,10 @@ export default class MeasureDrilldown extends React.Component { <li> <Link activeClassName="active" - to={{ pathname: `metric/${metric.key}/bubbles`, query: { id: component.key } }}> + to={{ + pathname: `/component_measures/metric/${metric.key}/bubbles`, + query: { id: component.key } + }}> <IconBubbles/> {translate('component_measures.tab.bubbles')} </Link> @@ -73,7 +76,10 @@ export default class MeasureDrilldown extends React.Component { <li> <Link activeClassName="active" - to={{ pathname: `metric/${metric.key}/treemap`, query: { id: component.key } }}> + to={{ + pathname: `/component_measures/metric/${metric.key}/treemap`, + query: { id: component.key } + }}> <IconTreemap/> {translate('component_measures.tab.treemap')} </Link> @@ -84,7 +90,10 @@ export default class MeasureDrilldown extends React.Component { <li> <Link activeClassName="active" - to={{ pathname: `metric/${metric.key}/history`, query: { id: component.key } }}> + to={{ + pathname: `/component_measures/metric/${metric.key}/history`, + query: { id: component.key } + }}> <IconHistory/> {translate('component_measures.tab.history')} </Link> diff --git a/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/TreeViewContainer.js b/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/TreeViewContainer.js index 4900542ad24..114b9ef08e2 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/TreeViewContainer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/details/drilldown/TreeViewContainer.js @@ -18,33 +18,39 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { connect } from 'react-redux'; -import pick from '../../../../../../../node_modules/lodash/pick'; - import TreeView from './TreeView'; import { - start, - drilldown, - useBreadcrumbs, - fetchMore, - selectComponent, - selectNext, - selectPrevious + start, + drilldown, + useBreadcrumbs, + fetchMore, + selectComponent, + selectNext, + selectPrevious } from '../../store/treeViewActions'; +import { + getMeasuresAppTreeComponents, + getMeasuresAppTreeBreadcrumbs, + getMeasuresAppTreeSelected, + getMeasuresAppTreeTotal, + getMeasuresAppTreePageIndex, + getMeasuresAppAllMetrics, + getMeasuresAppDetailsMetric, + isMeasuresAppFetching + , getMeasuresAppComponent +} from '../../../../app/store/rootReducer'; const mapStateToProps = state => { - const drilldown = pick(state.tree, [ - 'components', - 'breadcrumbs', - 'selected', - 'total', - 'pageIndex' - ]); return { - ...drilldown, - component: state.app.component, - metrics: state.app.metrics, - metric: state.details.metric, - fetching: state.status.fetching + components: getMeasuresAppTreeComponents(state), + breadcrumbs: getMeasuresAppTreeBreadcrumbs(state), + selected: getMeasuresAppTreeSelected(state), + total: getMeasuresAppTreeTotal(state), + pageIndex: getMeasuresAppTreePageIndex(state), + component: getMeasuresAppComponent(state), + metrics: getMeasuresAppAllMetrics(state), + metric: getMeasuresAppDetailsMetric(state), + fetching: isMeasuresAppFetching(state) }; }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/details/history/MeasureHistoryContainer.js b/server/sonar-web/src/main/js/apps/component-measures/details/history/MeasureHistoryContainer.js index bfe7decf1a3..b70189eedb5 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/details/history/MeasureHistoryContainer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/details/history/MeasureHistoryContainer.js @@ -18,13 +18,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { connect } from 'react-redux'; - import MeasureHistory from './MeasureHistory'; +import { getMeasuresAppDetailsMetric, getMeasuresAppComponent } from '../../../../app/store/rootReducer'; const mapStateToProps = state => { return { - component: state.app.component, - metric: state.details.metric + component: getMeasuresAppComponent(state), + metric: getMeasuresAppDetailsMetric(state) }; }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/details/treemap/MeasureTreemapContainer.js b/server/sonar-web/src/main/js/apps/component-measures/details/treemap/MeasureTreemapContainer.js index 978a2a522e2..e24858b82e3 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/details/treemap/MeasureTreemapContainer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/details/treemap/MeasureTreemapContainer.js @@ -18,13 +18,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { connect } from 'react-redux'; - import MeasureTreemap from './MeasureTreemap'; +import { getMeasuresAppDetailsMetric, getMeasuresAppComponent } from '../../../../app/store/rootReducer'; const mapStateToProps = state => { return { - component: state.app.component, - metric: state.details.metric + component: getMeasuresAppComponent(state), + metric: getMeasuresAppDetailsMetric(state) }; }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/home/AllMeasuresContainer.js b/server/sonar-web/src/main/js/apps/component-measures/home/AllMeasuresContainer.js index 00bb948b70b..d02d06bfcc3 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/home/AllMeasuresContainer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/home/AllMeasuresContainer.js @@ -18,14 +18,18 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { connect } from 'react-redux'; - import AllMeasures from './AllMeasures'; +import { + getMeasuresAppHomeDomains, + getMeasuresAppHomePeriods, + getMeasuresAppComponent +} from '../../../app/store/rootReducer'; const mapStateToProps = state => { return { - component: state.app.component, - domains: state.home.domains, - periods: state.home.periods + component: getMeasuresAppComponent(state), + domains: getMeasuresAppHomeDomains(state), + periods: getMeasuresAppHomePeriods(state) }; }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/home/DomainMeasuresContainer.js b/server/sonar-web/src/main/js/apps/component-measures/home/DomainMeasuresContainer.js index f71d24c0368..53094eb9bc7 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/home/DomainMeasuresContainer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/home/DomainMeasuresContainer.js @@ -18,14 +18,18 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { connect } from 'react-redux'; - import DomainMeasures from './DomainMeasures'; +import { + getMeasuresAppHomeDomains, + getMeasuresAppHomePeriods, + getMeasuresAppComponent +} from '../../../app/store/rootReducer'; const mapStateToProps = state => { return { - component: state.app.component, - domains: state.home.domains, - periods: state.home.periods + component: getMeasuresAppComponent(state), + domains: getMeasuresAppHomeDomains(state), + periods: getMeasuresAppHomePeriods(state) }; }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/home/Home.js b/server/sonar-web/src/main/js/apps/component-measures/home/Home.js index 84b8dabece3..6b1faf5402f 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/home/Home.js +++ b/server/sonar-web/src/main/js/apps/component-measures/home/Home.js @@ -51,7 +51,7 @@ export default class Home extends React.Component { <ul> <li> <IndexLink - to={{ pathname: '/', query: { id: component.key } }} + to={{ pathname: '/component_measures', query: { id: component.key } }} activeClassName="active"> {translate('all')} </IndexLink> @@ -59,7 +59,7 @@ export default class Home extends React.Component { {domains.map(domain => ( <li key={domain.name}> <Link - to={{ pathname: `domain/${domain.name}`, query: { id: component.key } }} + to={{ pathname: `/component_measures/domain/${domain.name}`, query: { id: component.key } }} activeClassName="active"> {getLocalizedMetricDomain(domain.name)} </Link> diff --git a/server/sonar-web/src/main/js/apps/component-measures/home/HomeContainer.js b/server/sonar-web/src/main/js/apps/component-measures/home/HomeContainer.js index a5473ec3dba..58e3285c181 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/home/HomeContainer.js +++ b/server/sonar-web/src/main/js/apps/component-measures/home/HomeContainer.js @@ -18,16 +18,20 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { connect } from 'react-redux'; - import Home from './Home'; import { fetchMeasures } from './actions'; import { displayHome } from '../app/actions'; +import { + getMeasuresAppHomeDomains, + getMeasuresAppHomePeriods, + getMeasuresAppComponent +} from '../../../app/store/rootReducer'; const mapStateToProps = state => { return { - component: state.app.component, - domains: state.home.domains, - periods: state.home.periods + component: getMeasuresAppComponent(state), + domains: getMeasuresAppHomeDomains(state), + periods: getMeasuresAppHomePeriods(state) }; }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/home/MeasuresList.js b/server/sonar-web/src/main/js/apps/component-measures/home/MeasuresList.js index 95a0ac857a8..42d3f46e94f 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/home/MeasuresList.js +++ b/server/sonar-web/src/main/js/apps/component-measures/home/MeasuresList.js @@ -30,7 +30,7 @@ const MeasuresList = ({ measures, component, className = 'domain-measures' }) => <li key={measure.metric.key} id={`measure-${measure.metric.key}`}> - <Link to={{ pathname: `metric/${measure.metric.key}`, query: { id: component.key } }}> + <Link to={{ pathname: `/component_measures/metric/${measure.metric.key}`, query: { id: component.key } }}> <div className="domain-measures-name"> <span id={`measure-${measure.metric.key}-name`}> {getLocalizedMetricName(measure.metric)} diff --git a/server/sonar-web/src/main/js/apps/component-measures/home/actions.js b/server/sonar-web/src/main/js/apps/component-measures/home/actions.js index 31a9309c900..006d4680523 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/home/actions.js +++ b/server/sonar-web/src/main/js/apps/component-measures/home/actions.js @@ -21,8 +21,9 @@ import { startFetching, stopFetching } from '../store/statusActions'; import { getMeasuresAndMeta } from '../../../api/measures'; import { getLeakPeriod } from '../../../helpers/periods'; import { getLeakValue } from '../utils'; +import { getMeasuresAppComponent, getMeasuresAppAllMetrics } from '../../../app/store/rootReducer'; -export const RECEIVE_MEASURES = 'home/RECEIVE_MEASURES'; +export const RECEIVE_MEASURES = 'measuresApp/home/RECEIVE_MEASURES'; export function receiveMeasures (measures, periods) { return { type: RECEIVE_MEASURES, measures, periods }; @@ -39,7 +40,10 @@ export function fetchMeasures () { return (dispatch, getState) => { dispatch(startFetching()); - const { component, metrics } = getState().app; + const state = getState(); + const component = getMeasuresAppComponent(state); + const metrics = getMeasuresAppAllMetrics(state); + const metricKeys = metrics .filter(metric => !metric.hidden) .filter(metric => metric.type !== 'DATA' && metric.type !== 'DISTRIB') diff --git a/server/sonar-web/src/main/js/apps/component-measures/routes.js b/server/sonar-web/src/main/js/apps/component-measures/routes.js new file mode 100644 index 00000000000..e2e24f96034 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-measures/routes.js @@ -0,0 +1,49 @@ +/* + * 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 { Route, IndexRoute, IndexRedirect } from 'react-router'; +import AppContainer from './app/AppContainer'; +import HomeContainer from './home/HomeContainer'; +import AllMeasuresContainer from './home/AllMeasuresContainer'; +import DomainMeasuresContainer from './home/DomainMeasuresContainer'; +import MeasureDetailsContainer from './details/MeasureDetailsContainer'; +import ListViewContainer from './details/drilldown/ListViewContainer'; +import TreeViewContainer from './details/drilldown/TreeViewContainer'; +import MeasureHistoryContainer from './details/history/MeasureHistoryContainer'; +import MeasureTreemapContainer from './details/treemap/MeasureTreemapContainer'; +import { checkHistoryExistence } from './hooks'; +import './styles.css'; + +export default ( + <Route component={AppContainer}> + <Route component={HomeContainer}> + <IndexRoute component={AllMeasuresContainer}/> + <Route path="domain/:domainName" component={DomainMeasuresContainer}/> + </Route> + + <Route path="metric/:metricKey" component={MeasureDetailsContainer}> + <IndexRedirect to="list"/> + <Route path="list" component={ListViewContainer}/> + <Route path="tree" component={TreeViewContainer}/> + <Route path="history" component={MeasureHistoryContainer} onEnter={checkHistoryExistence}/> + <Route path="treemap" component={MeasureTreemapContainer}/> + </Route> + </Route> +); diff --git a/server/sonar-web/src/main/js/apps/component-measures/store/configureStore.js b/server/sonar-web/src/main/js/apps/component-measures/store/configureStore.js deleted file mode 100644 index 07d55655531..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/store/configureStore.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 appReducer from './../app/reducer'; -import statusReducer from './statusReducer'; -import homeReducer from '../home/reducer'; -import detailsReducer from '../details/reducer'; -import listViewReducer from './listViewReducer'; -import treeViewReducer from './treeViewReducer'; -import configureStore from '../../../components/store/configureStore'; - -export default function customConfigureStore (initialState) { - const rootReducer = combineReducers({ - app: appReducer, - home: homeReducer, - details: detailsReducer, - list: listViewReducer, - tree: treeViewReducer, - status: statusReducer - }); - - return configureStore(rootReducer, initialState); -} diff --git a/server/sonar-web/src/main/js/apps/component-measures/store/listViewActions.js b/server/sonar-web/src/main/js/apps/component-measures/store/listViewActions.js index 8e61e53dd0a..1f48656bfb8 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/store/listViewActions.js +++ b/server/sonar-web/src/main/js/apps/component-measures/store/listViewActions.js @@ -21,8 +21,9 @@ import { getComponentTree } from '../../../api/components'; import { enhanceWithMeasure } from '../utils'; import { startFetching, stopFetching } from './statusActions'; import complementary from '../config/complementary'; +import { getMeasuresAppList } from '../../../app/store/rootReducer'; -export const UPDATE_STORE = 'drilldown/list/UPDATE_STORE'; +export const UPDATE_STORE = 'measuresApp/drilldown/list/UPDATE_STORE'; function updateStore (state) { return { type: UPDATE_STORE, state }; @@ -77,7 +78,7 @@ function fetchLeaves (baseComponent, metric, pageIndex = 1, periodIndex = 1) { */ export function fetchList (baseComponent, metric, periodIndex = 1) { return (dispatch, getState) => { - const { list } = getState(); + const list = getMeasuresAppList(getState()); if (list.baseComponent === baseComponent && list.metric === metric) { return Promise.resolve(); } @@ -96,7 +97,7 @@ export function fetchList (baseComponent, metric, periodIndex = 1) { export function fetchMore (periodIndex) { return (dispatch, getState) => { - const { baseComponent, metric, pageIndex, components } = getState().list; + const { baseComponent, metric, pageIndex, components } = getMeasuresAppList(getState()); dispatch(startFetching()); return fetchLeaves(baseComponent, metric, pageIndex + 1, periodIndex).then(r => { const nextComponents = [...components, ...r.components]; @@ -121,7 +122,7 @@ export function selectComponent (component) { */ export function selectNext () { return (dispatch, getState) => { - const { components, selected } = getState().list; + const { components, selected } = getMeasuresAppList(getState()); const selectedIndex = components.indexOf(selected); if (selectedIndex < components.length - 1) { const nextSelected = components[selectedIndex + 1]; @@ -135,7 +136,7 @@ export function selectNext () { */ export function selectPrevious () { return (dispatch, getState) => { - const { components, selected } = getState().list; + const { components, selected } = getMeasuresAppList(getState()); const selectedIndex = components.indexOf(selected); if (selectedIndex > 0) { const nextSelected = components[selectedIndex - 1]; diff --git a/server/sonar-web/src/main/js/apps/component-measures/store/rootReducer.js b/server/sonar-web/src/main/js/apps/component-measures/store/rootReducer.js new file mode 100644 index 00000000000..f57fef25a4a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-measures/store/rootReducer.js @@ -0,0 +1,116 @@ +/* + * 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 appReducer from './../app/reducer'; +import statusReducer from './statusReducer'; +import homeReducer from '../home/reducer'; +import detailsReducer from '../details/reducer'; +import listViewReducer from './listViewReducer'; +import treeViewReducer from './treeViewReducer'; + +export default combineReducers({ + app: appReducer, + home: homeReducer, + details: detailsReducer, + list: listViewReducer, + tree: treeViewReducer, + status: statusReducer +}); + +export const getComponent = state => ( + state.app.component +); + +export const getAllMetrics = state => ( + state.app.metrics +); + +export const getDetailsMetric = state => ( + state.details.metric +); + +export const getDetailsMeasure = state => ( + state.details.measure +); + +export const getDetailsSecondaryMeasure = state => ( + state.details.secondaryMeasure +); + +export const getDetailsPeriods = state => ( + state.details.periods +); + +export const isFetching = state => ( + state.status.fetching +); + +export const getList = state => ( + state.list +); + +export const getListComponents = state => ( + state.list.components +); + +export const getListSelected = state => ( + state.list.selected +); + +export const getListTotal = state => ( + state.list.total +); + +export const getListPageIndex = state => ( + state.list.pageIndex +); + +export const getTree = state => ( + state.tree +); + +export const getTreeComponents = state => ( + state.tree.components +); + +export const getTreeBreadcrumbs = state => ( + state.tree.breadcrumbs +); + +export const getTreeSelected = state => ( + state.tree.selected +); + +export const getTreeTotal = state => ( + state.tree.total +); + +export const getTreePageIndex = state => ( + state.tree.pageIndex +); + +export const getHomeDomains = state => ( + state.home.domains +); + +export const getHomePeriods = state => ( + state.home.periods +); diff --git a/server/sonar-web/src/main/js/apps/component-measures/store/statusActions.js b/server/sonar-web/src/main/js/apps/component-measures/store/statusActions.js index 5755ad5ff55..83eb605cb7c 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/store/statusActions.js +++ b/server/sonar-web/src/main/js/apps/component-measures/store/statusActions.js @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -export const START_FETCHING = 'status/START_FETCHING'; -export const STOP_FETCHING = 'status/STOP_FETCHING'; +export const START_FETCHING = 'measuresApp/status/START_FETCHING'; +export const STOP_FETCHING = 'measuresApp/status/STOP_FETCHING'; export function startFetching () { return { type: START_FETCHING }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/store/treeViewActions.js b/server/sonar-web/src/main/js/apps/component-measures/store/treeViewActions.js index 49cc3ff504d..81d2896c9d8 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/store/treeViewActions.js +++ b/server/sonar-web/src/main/js/apps/component-measures/store/treeViewActions.js @@ -23,13 +23,14 @@ import { getComponentTree } from '../../../api/components'; import { enhanceWithMeasure } from '../utils'; import { startFetching, stopFetching } from './statusActions'; import complementary from '../config/complementary'; +import { getMeasuresAppTree } from '../../../app/store/rootReducer'; /* * Actions */ -export const UPDATE_STORE = 'drilldown/tree/UPDATE_STORE'; -export const INIT = 'drilldown/tree/INIT'; +export const UPDATE_STORE = 'measuresApp/drilldown/tree/UPDATE_STORE'; +export const INIT = 'measuresApp/drilldown/tree/INIT'; /* * Action Creators @@ -115,7 +116,7 @@ function fetchComponents (rootComponent, baseComponent, metric, pageIndex = 1, p */ function fetchList (baseComponent) { return (dispatch, getState) => { - const { metric, periodIndex, rootComponent } = getState().tree; + const { metric, periodIndex, rootComponent } = getMeasuresAppTree(getState()); dispatch(startFetching()); return fetchComponents(rootComponent, baseComponent, metric, 1, periodIndex).then(r => { @@ -139,7 +140,7 @@ function fetchList (baseComponent) { */ export function start (rootComponent, metric, periodIndex = 1) { return (dispatch, getState) => { - const { tree } = getState(); + const tree = getMeasuresAppTree(getState()); if (rootComponent === tree.rootComponent && metric === tree.metric) { return Promise.resolve(); } @@ -155,7 +156,7 @@ export function start (rootComponent, metric, periodIndex = 1) { */ export function drilldown (component) { return (dispatch, getState) => { - const { metric, rootComponent, breadcrumbs, periodIndex } = getState().tree; + const { metric, rootComponent, breadcrumbs, periodIndex } = getMeasuresAppTree(getState()); dispatch(startFetching()); return fetchComponents(rootComponent, component, metric, 1, periodIndex).then(r => { dispatch(updateStore({ @@ -174,7 +175,7 @@ export function drilldown (component) { */ export function useBreadcrumbs (component) { return (dispatch, getState) => { - const { metric, rootComponent, breadcrumbs, periodIndex } = getState().tree; + const { metric, rootComponent, breadcrumbs, periodIndex } = getMeasuresAppTree(getState()); const index = breadcrumbs.indexOf(component); dispatch(startFetching()); return fetchComponents(rootComponent, component, metric, 1, periodIndex).then(r => { @@ -190,7 +191,7 @@ export function useBreadcrumbs (component) { export function fetchMore () { return (dispatch, getState) => { - const { rootComponent, baseComponent, metric, pageIndex, components, periodIndex } = getState().tree; + const { rootComponent, baseComponent, metric, pageIndex, components, periodIndex } = getMeasuresAppTree(getState()); dispatch(startFetching()); return fetchComponents(rootComponent, baseComponent, metric, pageIndex + 1, periodIndex).then(r => { const nextComponents = [...components, ...r.components]; @@ -206,7 +207,7 @@ export function fetchMore () { */ export function selectComponent (component) { return (dispatch, getState) => { - const { breadcrumbs } = getState().tree; + const { breadcrumbs } = getMeasuresAppTree(getState()); const nextBreadcrumbs = [...breadcrumbs, component]; dispatch(updateStore({ selected: component, @@ -220,7 +221,7 @@ export function selectComponent (component) { */ export function selectNext () { return (dispatch, getState) => { - const { components, selected, breadcrumbs } = getState().tree; + const { components, selected, breadcrumbs } = getMeasuresAppTree(getState()); const selectedIndex = components.indexOf(selected); if (selectedIndex < components.length - 1) { const nextSelected = components[selectedIndex + 1]; @@ -238,7 +239,7 @@ export function selectNext () { */ export function selectPrevious () { return (dispatch, getState) => { - const { components, selected, breadcrumbs } = getState().tree; + const { components, selected, breadcrumbs } = getMeasuresAppTree(getState()); const selectedIndex = components.indexOf(selected); if (selectedIndex > 0) { const nextSelected = components[selectedIndex - 1]; diff --git a/server/sonar-web/src/main/js/apps/custom-measures/components/CustomMeasuresAppContainer.js b/server/sonar-web/src/main/js/apps/custom-measures/components/CustomMeasuresAppContainer.js new file mode 100644 index 00000000000..a88477e394a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/custom-measures/components/CustomMeasuresAppContainer.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 React from 'react'; +import init from '../init'; + +export default class CustomMeasuresAppContainer extends React.Component { + componentDidMount () { + if (this.props.component) { + init(this.refs.container, this.props.component); + } + } + + componentDidUpdate () { + if (this.props.component) { + init(this.refs.container, this.props.component); + } + } + + render () { + return <div ref="container"/>; + } +} diff --git a/server/sonar-web/src/main/js/apps/custom-measures/app.js b/server/sonar-web/src/main/js/apps/custom-measures/init.js index 531584e81d4..caf512737a0 100644 --- a/server/sonar-web/src/main/js/apps/custom-measures/app.js +++ b/server/sonar-web/src/main/js/apps/custom-measures/init.js @@ -25,22 +25,20 @@ import ListView from './list-view'; import ListFooterView from './list-footer-view'; const App = new Marionette.Application(); -const init = function (options) { +const init = function (el, component) { // Layout - this.layout = new Layout({ - el: options.el - }); + this.layout = new Layout({ el }); this.layout.render(); // Collection this.customMeasures = new CustomMeasures({ - projectId: options.component.id + projectId: component.id }); // Header View this.headerView = new HeaderView({ collection: this.customMeasures, - projectId: options.component.id + projectId: component.id }); this.layout.headerRegion.show(this.headerView); @@ -61,8 +59,10 @@ const init = function (options) { }; App.on('start', function (options) { - init.call(App, options); + init.call(App, options.el, options.component); }); -window.sonarqube.appStarted.then(options => App.start(options)); +export default function (el, component) { + App.start({ el, component }); +} diff --git a/server/sonar-web/src/main/js/apps/custom-measures/routes.js b/server/sonar-web/src/main/js/apps/custom-measures/routes.js new file mode 100644 index 00000000000..09b916d711d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/custom-measures/routes.js @@ -0,0 +1,26 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import CustomMeasuresAppContainer from './components/CustomMeasuresAppContainer'; + +export default ( + <IndexRoute component={CustomMeasuresAppContainer}/> +); diff --git a/server/sonar-web/src/main/js/apps/groups/components/GroupsAppContainer.js b/server/sonar-web/src/main/js/apps/groups/components/GroupsAppContainer.js new file mode 100644 index 00000000000..55cbb49127e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/components/GroupsAppContainer.js @@ -0,0 +1,31 @@ +/* + * 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 init from '../init'; + +export default class GroupsAppContainer extends React.Component { + componentDidMount () { + init(this.refs.container); + } + + render () { + return <div ref="container"/>; + } +} diff --git a/server/sonar-web/src/main/js/apps/groups/app.js b/server/sonar-web/src/main/js/apps/groups/init.js index c9d8562646f..4ec6a7e95e6 100644 --- a/server/sonar-web/src/main/js/apps/groups/app.js +++ b/server/sonar-web/src/main/js/apps/groups/init.js @@ -26,11 +26,9 @@ import ListView from './list-view'; import ListFooterView from './list-footer-view'; const App = new Marionette.Application(); -const init = function () { - const options = window.sonarqube; - +const init = function (el) { // Layout - this.layout = new Layout({ el: options.el }); + this.layout = new Layout({ el }); this.layout.render(); // Collection @@ -56,9 +54,11 @@ const init = function () { this.groups.fetch(); }; -App.on('start', function () { - init.call(App); +App.on('start', function (el) { + init.call(App, el); }); -window.sonarqube.appStarted.then(options => App.start(options)); +export default function (el) { + App.start(el); +} diff --git a/server/sonar-web/src/main/js/apps/groups/routes.js b/server/sonar-web/src/main/js/apps/groups/routes.js new file mode 100644 index 00000000000..08981267c79 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/groups/routes.js @@ -0,0 +1,26 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import GroupsAppContainer from './components/GroupsAppContainer'; + +export default ( + <IndexRoute component={GroupsAppContainer}/> +); diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesAppContainer.js b/server/sonar-web/src/main/js/apps/issues/components/IssuesAppContainer.js new file mode 100644 index 00000000000..33820285418 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesAppContainer.js @@ -0,0 +1,31 @@ +/* + * 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 init from '../init'; + +export default class IssuesAppContainer extends React.Component { + componentDidMount () { + init(this.refs.container); + } + + render () { + return <div ref="container"/>; + } +} diff --git a/server/sonar-web/src/main/js/apps/issues/app.js b/server/sonar-web/src/main/js/apps/issues/init.js index 4623b55988f..2ac5041b6ef 100644 --- a/server/sonar-web/src/main/js/apps/issues/app.js +++ b/server/sonar-web/src/main/js/apps/issues/init.js @@ -32,14 +32,12 @@ import FacetsView from './facets-view'; import HeaderView from './HeaderView'; const App = new Marionette.Application(); -const init = function () { - const options = window.sonarqube; - +const init = function (el) { this.state = new State({ canBulkChange: !!window.SS.user }); this.list = new Issues(); this.facets = new Facets(); - this.layout = new Layout({ app: this, el: options.el }); + this.layout = new Layout({ app: this, el }); this.layout.render(); $('#footer').addClass('search-navigator-footer'); @@ -74,9 +72,11 @@ const init = function () { Backbone.history.start(); }; -App.on('start', function () { - init.call(App); +App.on('start', function (el) { + init.call(App, el); }); -window.sonarqube.appStarted.then(options => App.start(options)); +export default function (el) { + App.start(el); +} diff --git a/server/sonar-web/src/main/js/apps/issues/routes.js b/server/sonar-web/src/main/js/apps/issues/routes.js new file mode 100644 index 00000000000..a05f0ec92d2 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/issues/routes.js @@ -0,0 +1,26 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import IssuesAppContainer from './components/IssuesAppContainer'; + +export default ( + <IndexRoute component={IssuesAppContainer}/> +); diff --git a/server/sonar-web/src/main/js/apps/maintenance/components/MaintenanceAppContainer.js b/server/sonar-web/src/main/js/apps/maintenance/components/MaintenanceAppContainer.js new file mode 100644 index 00000000000..1419251f259 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/maintenance/components/MaintenanceAppContainer.js @@ -0,0 +1,31 @@ +/* + * 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 init from '../init'; + +export default class MaintenanceAppContainer extends React.Component { + componentDidMount () { + init(this.refs.container, false); + } + + render () { + return <div ref="container"/>; + } +} diff --git a/server/sonar-web/src/main/js/apps/markdown/app.js b/server/sonar-web/src/main/js/apps/maintenance/components/SetupAppContainer.js index e6b264a6885..4fb0c472b00 100644 --- a/server/sonar-web/src/main/js/apps/markdown/app.js +++ b/server/sonar-web/src/main/js/apps/maintenance/components/SetupAppContainer.js @@ -17,15 +17,15 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import Marionette from 'backbone.marionette'; -import MarkdownView from './markdown-help-view'; +import React from 'react'; +import init from '../init'; -const App = new Marionette.Application(); - -App.on('start', function () { - const options = window.sonarqube; - new MarkdownView({ el: options.el }).render(); -}); - -window.sonarqube.appStarted.then(options => App.start(options)); +export default class SetupAppContainer extends React.Component { + componentDidMount () { + init(this.refs.container, true); + } + render () { + return <div ref="container"/>; + } +} diff --git a/server/sonar-web/src/main/js/apps/maintenance/app.js b/server/sonar-web/src/main/js/apps/maintenance/init.js index 6ed1c7dbce0..3b812525570 100644 --- a/server/sonar-web/src/main/js/apps/maintenance/app.js +++ b/server/sonar-web/src/main/js/apps/maintenance/init.js @@ -24,9 +24,7 @@ import MainView from './main-view'; const App = new Marionette.Application(); -App.on('start', function () { - const options = window.sonarqube; - +App.on('start', function (options) { const viewOptions = _.extend(options, { model: new Backbone.Model() }); @@ -34,5 +32,7 @@ App.on('start', function () { mainView.render().refresh(); }); -App.start(); +export default function (el, setup) { + App.start({ el, setup }); +} diff --git a/server/sonar-web/src/main/js/apps/maintenance/routes.js b/server/sonar-web/src/main/js/apps/maintenance/routes.js new file mode 100644 index 00000000000..259894f544c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/maintenance/routes.js @@ -0,0 +1,31 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import MaintenanceAppContainer from './components/MaintenanceAppContainer'; +import SetupAppContainer from './components/SetupAppContainer'; + +export const maintenanceRoutes = ( + <IndexRoute component={MaintenanceAppContainer}/> +); + +export const setupRoutes = ( + <IndexRoute component={SetupAppContainer}/> +); diff --git a/server/sonar-web/src/main/js/apps/markdown/templates/markdown-help.hbs b/server/sonar-web/src/main/js/apps/markdown/templates/markdown-help.hbs deleted file mode 100644 index dbcabaf2227..00000000000 --- a/server/sonar-web/src/main/js/apps/markdown/templates/markdown-help.hbs +++ /dev/null @@ -1,100 +0,0 @@ -<h2 class="spacer-bottom">Markdown Syntax</h2> -<table class="width-100 data zebra"> - <thead> - <tr> - <th>Write:</th> - <th>To display:</th> - </tr> - </thead> - <tbody> - <tr> - <td>*this text is bold*</td> - <td class="markdown"><strong>this text is bold</strong></td> - </tr> - <tr> - <td>http://sonarqube.org</td> - <td class="markdown"><a href="http://sonarqube.org">http://sonarqube.org</a></td> - </tr> - <tr> - <td class="text-top"> - [SonarQubeâ„¢ Home Page](http://www.sonarqube.org) - </td> - <td class="markdown text-top"> - <a href="http://www.sonarqube.org" target="_blank">SonarQubeâ„¢ Home Page</a> - </td> - </tr> - <tr> - <td class="text-top">* first item<br> - * second item - </td> - <td class="markdown"> - <ul> - <li>first item</li> - <li>second item</li> - </ul> - </td> - </tr> - <tr> - <td class="text-top">1. first item<br> - 1. second item - </td> - <td class="markdown text-top"> - <ol> - <li>first item</li> - <li>second item</li> - </ol> - </td> - </tr> - <tr> - <td class="text-top"> - = Heading Level 1<br> - == Heading Level 2<br> - === Heading Level 3<br> - ==== Heading Level 4<br> - ===== Heading Level 5<br> - ====== Heading Level 6<br> - <td class="markdown text-top"> - <h1>Heading Level 1</h1> - <h2>Heading Level 2</h2> - <h3>Heading Level 3</h3> - <h4>Heading Level 4</h4> - <h5>Heading Level 5</h5> - <h6>Heading Level 6</h6> - </td> - </tr> - <tr> - <td class="text-top">``Lists#newArrayList()``</td> - <td class="markdown text-top"><code>Lists#newArrayList()</code></td> - </tr> - <tr> - <td class="text-top"> - ``<br> - // code on multiple lines<br> - public void foo() {<br> - // do some logic here<br> - }<br> - `` - </td> - <td class="markdown text-top"> -<pre> - // code on multiple lines - public void foo() { - // do some logic here - } -</pre> - </td> - </tr> - <tr> - <td class="text-top"> - Standard text<br> - > Blockquoted text<br> - > that spans multiple lines<br> - </td> - <td class="markdown text-top"> - <p>Standard text</p> - <blockquote>Blockquoted text<br> - that spans multiple lines<br></blockquote> - </td> - </tr> - </tbody> -</table> diff --git a/server/sonar-web/src/main/js/apps/metrics/components/MetricsAppContainer.js b/server/sonar-web/src/main/js/apps/metrics/components/MetricsAppContainer.js new file mode 100644 index 00000000000..765ead2248a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/components/MetricsAppContainer.js @@ -0,0 +1,31 @@ +/* + * 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 init from '../init'; + +export default class MetricsAppContainer extends React.Component { + componentDidMount () { + init(this.refs.container); + } + + render () { + return <div ref="container"/>; + } +} diff --git a/server/sonar-web/src/main/js/apps/metrics/app.js b/server/sonar-web/src/main/js/apps/metrics/init.js index 542a5073d17..06f12f34ff2 100644 --- a/server/sonar-web/src/main/js/apps/metrics/app.js +++ b/server/sonar-web/src/main/js/apps/metrics/init.js @@ -26,11 +26,9 @@ import ListView from './list-view'; import ListFooterView from './list-footer-view'; const App = new Marionette.Application(); -const init = function () { - const options = window.sonarqube; - +const init = function (el) { // Layout - this.layout = new Layout({ el: options.el }); + this.layout = new Layout({ el }); this.layout.render(); // Collection @@ -72,11 +70,13 @@ App.requestTypes = function () { }); }; -App.on('start', function () { +App.on('start', function (el) { $.when(App.requestDomains(), App.requestTypes()).done(function () { - init.call(App); + init.call(App, el); }); }); -window.sonarqube.appStarted.then(options => App.start(options)); +export default function (el) { + App.start(el); +} diff --git a/server/sonar-web/src/main/js/apps/metrics/routes.js b/server/sonar-web/src/main/js/apps/metrics/routes.js new file mode 100644 index 00000000000..84955baa23e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/metrics/routes.js @@ -0,0 +1,26 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import MetricsAppContainer from './components/MetricsAppContainer'; + +export default ( + <IndexRoute component={MetricsAppContainer}/> +); diff --git a/server/sonar-web/src/main/js/apps/permissions/global/app.js b/server/sonar-web/src/main/js/apps/overview/components/AppContainer.js index db8e2981d9c..85d09db946d 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/app.js +++ b/server/sonar-web/src/main/js/apps/overview/components/AppContainer.js @@ -18,18 +18,26 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import { render } from 'react-dom'; -import { Provider } from 'react-redux'; -import App from './components/App'; -import configureStore from '../../../components/store/configureStore'; -import rootReducer from '../shared/store/rootReducer'; +import App from './App'; -window.sonarqube.appStarted.then(options => { - const el = document.querySelector(options.el); - const store = configureStore(rootReducer); - render(( - <Provider store={store}> - <App/> - </Provider> - ), el); -}); +export default class AppContainer extends React.Component { + state = {}; + + componentDidMount () { + window.sonarqube.appStarted.then(options => { + this.setState({ component: options.component }); + }); + } + + render () { + if (!this.state.component) { + return null; + } + + const component = { ...this.state.component, ...window.sonarqube.overview.component }; + + return ( + <App component={component}/> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/overview/routes.js b/server/sonar-web/src/main/js/apps/overview/routes.js new file mode 100644 index 00000000000..6fac11a62ab --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/routes.js @@ -0,0 +1,28 @@ +/* + * 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 { IndexRoute, Route } from 'react-router'; +import AppContainer from './components/AppContainer'; + +export default [ + <IndexRoute key="index" component={AppContainer}/>, + <Route key="1" path="index" component={AppContainer}/>, + <Route key="2" path="index/:projectKey" component={AppContainer}/> +]; diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.js b/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.js index bd538f0a2e0..d4d5b55c456 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.js @@ -152,7 +152,7 @@ export default class ActionsCell extends React.Component { {!this.props.fromDetails && ( <li> - <Link to={{ pathname: '/', query: { id: t.id } }}> + <Link to={{ pathname: '/permission_templates', query: { id: t.id } }}> {this.renderDropdownIcon(<i className="icon-edit"/>)} Edit Permissions </Link> diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/AppContainer.js b/server/sonar-web/src/main/js/apps/permission-templates/components/AppContainer.js new file mode 100644 index 00000000000..b6f4134f066 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/AppContainer.js @@ -0,0 +1,41 @@ +/* + * 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 App from './App'; + +export default class AppContainer extends React.Component { + state = {}; + + componentDidMount () { + window.sonarqube.appStarted.then(options => { + this.setState({ rootQualifiers: options.rootQualifiers }); + }); + } + + render () { + if (!this.state.rootQualifiers) { + return null; + } + + return ( + <App {...this.props} topQualifiers={this.state.rootQualifiers}/> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.js b/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.js index 8517610eb10..0e9a8b2e6be 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.js @@ -33,7 +33,7 @@ export default class NameCell extends React.Component { return ( <td> - <Link to={{ pathname: '/', query: { id: t.id } }}> + <Link to={{ pathname: '/permission_templates', query: { id: t.id } }}> <strong className="js-name">{t.name}</strong> </Link> diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.js b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.js index a92388d1f40..57dd6349760 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.js @@ -36,7 +36,7 @@ export default class TemplateHeader extends React.Component { return ( <header id="project-permissions-header" className="page-header"> <div className="note spacer-bottom"> - <Link to="/" className="text-muted"> + <Link to="/permission_templates" className="text-muted"> {translate('permission_templates.page')} </Link> </div> diff --git a/server/sonar-web/src/main/js/apps/permission-templates/routes.js b/server/sonar-web/src/main/js/apps/permission-templates/routes.js new file mode 100644 index 00000000000..799d82546f9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/routes.js @@ -0,0 +1,26 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import AppContainer from './components/AppContainer'; + +export default ( + <IndexRoute component={AppContainer}/> +); diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.js b/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.js index bb7b24fd24b..56343415027 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.js +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.js @@ -22,23 +22,23 @@ import { connect } from 'react-redux'; import SearchForm from '../../shared/components/SearchForm'; import HoldersList from '../../shared/components/HoldersList'; import { - loadHolders, - grantToUser, - revokeFromUser, - grantToGroup, - revokeFromGroup, - updateFilter, - updateQuery, - selectPermission + loadHolders, + grantToUser, + revokeFromUser, + grantToGroup, + revokeFromGroup, + updateFilter, + updateQuery, + selectPermission } from '../store/actions'; -import { - getUsers, - getGroups, - getQuery, - getFilter, - getSelectedPermission -} from '../../shared/store/rootReducer'; import { translate } from '../../../../helpers/l10n'; +import { + getPermissionsAppUsers, + getPermissionsAppGroups, + getPermissionsAppQuery, + getPermissionsAppFilter, + getPermissionsAppSelectedPermission +} from '../../../../app/store/rootReducer'; const PERMISSIONS_ORDER = [ 'admin', @@ -102,11 +102,11 @@ class AllHoldersList extends React.Component { } const mapStateToProps = state => ({ - users: getUsers(state), - groups: getGroups(state), - query: getQuery(state), - filter: getFilter(state), - selectedPermission: getSelectedPermission(state) + users: getPermissionsAppUsers(state), + groups: getPermissionsAppGroups(state), + query: getPermissionsAppQuery(state), + filter: getPermissionsAppFilter(state), + selectedPermission: getPermissionsAppSelectedPermission(state) }); const mapDispatchToProps = dispatch => ({ diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.js b/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.js index 23b750a2dd2..299de992836 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.js +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.js @@ -21,7 +21,7 @@ import React from 'react'; import { connect } from 'react-redux'; import { translate } from '../../../../helpers/l10n'; import { loadHolders } from '../store/actions'; -import { isLoading } from '../../shared/store/rootReducer'; +import { isPermissionsAppLoading } from '../../../../app/store/rootReducer'; class PageHeader extends React.Component { static propTypes = { @@ -53,7 +53,7 @@ class PageHeader extends React.Component { } const mapStateToProps = state => ({ - loading: isLoading(state) + loading: isPermissionsAppLoading(state) }); const mapDispatchToProps = dispatch => ({ diff --git a/server/sonar-web/src/main/js/apps/permissions/global/store/actions.js b/server/sonar-web/src/main/js/apps/permissions/global/store/actions.js index 3a7d1fa158d..207a0cf7e0a 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/store/actions.js +++ b/server/sonar-web/src/main/js/apps/permissions/global/store/actions.js @@ -19,19 +19,30 @@ */ import * as api from '../../../../api/permissions'; import { parseError } from '../../../code/utils'; -import { raiseError } from '../../shared/store/actions'; import { - getQuery, - getFilter, - getSelectedPermission -} from '../../shared/store/rootReducer'; + raiseError, + REQUEST_HOLDERS, + RECEIVE_HOLDERS_SUCCESS, + UPDATE_QUERY, + UPDATE_FILTER, + SELECT_PERMISSION, + GRANT_PERMISSION_TO_USER, + REVOKE_PERMISSION_TO_USER, + GRANT_PERMISSION_TO_GROUP, + REVOKE_PERMISSION_FROM_GROUP +} from '../../shared/store/actions'; +import { + getPermissionsAppQuery, + getPermissionsAppFilter, + getPermissionsAppSelectedPermission +} from '../../../../app/store/rootReducer'; export const loadHolders = () => (dispatch, getState) => { - const query = getQuery(getState()); - const filter = getFilter(getState()); - const selectedPermission = getSelectedPermission(getState()); + const query = getPermissionsAppQuery(getState()); + const filter = getPermissionsAppFilter(getState()); + const selectedPermission = getPermissionsAppSelectedPermission(getState()); - dispatch({ type: 'REQUEST_HOLDERS', query }); + dispatch({ type: REQUEST_HOLDERS, query }); const requests = []; @@ -49,7 +60,7 @@ export const loadHolders = () => (dispatch, getState) => { return Promise.all(requests).then(responses => ( dispatch({ - type: 'RECEIVE_HOLDERS_SUCCESS', + type: RECEIVE_HOLDERS_SUCCESS, users: responses[0], groups: responses[1], query @@ -60,30 +71,30 @@ export const loadHolders = () => (dispatch, getState) => { }; export const updateQuery = (query = '') => dispatch => { - dispatch({ type: 'UPDATE_QUERY', query }); + dispatch({ type: UPDATE_QUERY, query }); if (query.length === 0 || query.length > 2) { dispatch(loadHolders()); } }; export const updateFilter = filter => dispatch => { - dispatch({ type: 'UPDATE_FILTER', filter }); + dispatch({ type: UPDATE_FILTER, filter }); dispatch(loadHolders()); }; export const selectPermission = permission => (dispatch, getState) => { - const selectedPermission = getSelectedPermission(getState()); + const selectedPermission = getPermissionsAppSelectedPermission(getState()); if (selectedPermission !== permission) { - dispatch({ type: 'SELECT_PERMISSION', permission }); + dispatch({ type: SELECT_PERMISSION, permission }); } else { - dispatch({ type: 'SELECT_PERMISSION', permission: null }); + dispatch({ type: SELECT_PERMISSION, permission: null }); } dispatch(loadHolders()); }; export const grantToUser = (login, permission) => dispatch => { api.grantPermissionToUser(null, login, permission).then(() => { - dispatch({ type: 'GRANT_PERMISSION_TO_USER', login, permission }); + dispatch({ type: GRANT_PERMISSION_TO_USER, login, permission }); }).catch(e => { return parseError(e).then(message => dispatch(raiseError(message))); }); @@ -91,7 +102,7 @@ export const grantToUser = (login, permission) => dispatch => { export const revokeFromUser = (login, permission) => dispatch => { api.revokePermissionFromUser(null, login, permission).then(() => { - dispatch({ type: 'REVOKE_PERMISSION_TO_USER', login, permission }); + dispatch({ type: REVOKE_PERMISSION_TO_USER, login, permission }); }).catch(e => { return parseError(e).then(message => dispatch(raiseError(message))); }); @@ -100,7 +111,7 @@ export const revokeFromUser = (login, permission) => dispatch => { export const grantToGroup = (groupName, permission) => dispatch => { api.grantPermissionToGroup(null, groupName, permission).then(() => { dispatch({ - type: 'GRANT_PERMISSION_TO_GROUP', + type: GRANT_PERMISSION_TO_GROUP, groupName, permission }); @@ -112,7 +123,7 @@ export const grantToGroup = (groupName, permission) => dispatch => { export const revokeFromGroup = (groupName, permission) => dispatch => { api.revokePermissionFromGroup(null, groupName, permission).then(() => { dispatch({ - type: 'REVOKE_PERMISSION_FROM_GROUP', + type: REVOKE_PERMISSION_FROM_GROUP, groupName, permission }); diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/AllHoldersList.js b/server/sonar-web/src/main/js/apps/permissions/project/components/AllHoldersList.js index 6dcbe728084..63b041b2fcf 100644 --- a/server/sonar-web/src/main/js/apps/permissions/project/components/AllHoldersList.js +++ b/server/sonar-web/src/main/js/apps/permissions/project/components/AllHoldersList.js @@ -22,24 +22,24 @@ import { connect } from 'react-redux'; import SearchForm from '../../shared/components/SearchForm'; import HoldersList from '../../shared/components/HoldersList'; import { - loadHolders, - grantToUser, - revokeFromUser, - grantToGroup, - revokeFromGroup, - updateQuery, - updateFilter, - selectPermission + loadHolders, + grantToUser, + revokeFromUser, + grantToGroup, + revokeFromGroup, + updateQuery, + updateFilter, + selectPermission } from '../store/actions'; -import { - getUsers, - getGroups, - getQuery, - getFilter, - getSelectedPermission -} from '../../shared/store/rootReducer'; import { translate } from '../../../../helpers/l10n'; import { PERMISSIONS_ORDER_BY_QUALIFIER } from '../constants'; +import { + getPermissionsAppUsers, + getPermissionsAppGroups, + getPermissionsAppQuery, + getPermissionsAppFilter, + getPermissionsAppSelectedPermission +} from '../../../../app/store/rootReducer'; class AllHoldersList extends React.Component { static propTypes = { @@ -128,11 +128,11 @@ class AllHoldersList extends React.Component { } const mapStateToProps = state => ({ - users: getUsers(state), - groups: getGroups(state), - query: getQuery(state), - filter: getFilter(state), - selectedPermission: getSelectedPermission(state) + users: getPermissionsAppUsers(state), + groups: getPermissionsAppGroups(state), + query: getPermissionsAppQuery(state), + filter: getPermissionsAppFilter(state), + selectedPermission: getPermissionsAppSelectedPermission(state) }); const mapDispatchToProps = dispatch => ({ diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/App.js b/server/sonar-web/src/main/js/apps/permissions/project/components/App.js index d8bc447fa08..ba2b4e44e2e 100644 --- a/server/sonar-web/src/main/js/apps/permissions/project/components/App.js +++ b/server/sonar-web/src/main/js/apps/permissions/project/components/App.js @@ -27,15 +27,19 @@ import '../../styles.css'; export default class App extends React.Component { static propTypes = { - project: React.PropTypes.object.isRequired + component: React.PropTypes.object }; render () { + if (!this.props.component) { + return null; + } + return ( <div className="page page-limited"> - <PageHeader project={this.props.project}/> + <PageHeader project={this.props.component}/> <PageError/> - <AllHoldersList project={this.props.project}/> + <AllHoldersList project={this.props.component}/> </div> ); } diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.js b/server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.js index db60031625d..14fcf947ee9 100644 --- a/server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.js +++ b/server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.js @@ -22,7 +22,7 @@ import { connect } from 'react-redux'; import { translate } from '../../../../helpers/l10n'; import ApplyTemplateView from '../views/ApplyTemplateView'; import { loadHolders } from '../store/actions'; -import { isLoading } from '../../shared/store/rootReducer'; +import { isPermissionsAppLoading } from '../../../../app/store/rootReducer'; class PageHeader extends React.Component { static propTypes = { @@ -76,7 +76,7 @@ class PageHeader extends React.Component { } const mapStateToProps = state => ({ - loading: isLoading(state) + loading: isPermissionsAppLoading(state) }); const mapDispatchToProps = dispatch => ({ diff --git a/server/sonar-web/src/main/js/apps/permissions/project/store/actions.js b/server/sonar-web/src/main/js/apps/permissions/project/store/actions.js index 5011dc075ad..b705876a6a2 100644 --- a/server/sonar-web/src/main/js/apps/permissions/project/store/actions.js +++ b/server/sonar-web/src/main/js/apps/permissions/project/store/actions.js @@ -19,19 +19,30 @@ */ import * as api from '../../../../api/permissions'; import { parseError } from '../../../code/utils'; -import { raiseError } from '../../shared/store/actions'; import { - getQuery, - getFilter, - getSelectedPermission -} from '../../shared/store/rootReducer'; + raiseError, + REQUEST_HOLDERS, + RECEIVE_HOLDERS_SUCCESS, + UPDATE_QUERY, + UPDATE_FILTER, + SELECT_PERMISSION, + GRANT_PERMISSION_TO_USER, + REVOKE_PERMISSION_TO_USER, + GRANT_PERMISSION_TO_GROUP, + REVOKE_PERMISSION_FROM_GROUP +} from '../../shared/store/actions'; +import { + getPermissionsAppQuery, + getPermissionsAppFilter, + getPermissionsAppSelectedPermission +} from '../../../../app/store/rootReducer'; export const loadHolders = projectKey => (dispatch, getState) => { - const query = getQuery(getState()); - const filter = getFilter(getState()); - const selectedPermission = getSelectedPermission(getState()); + const query = getPermissionsAppQuery(getState()); + const filter = getPermissionsAppFilter(getState()); + const selectedPermission = getPermissionsAppSelectedPermission(getState()); - dispatch({ type: 'REQUEST_HOLDERS', query }); + dispatch({ type: REQUEST_HOLDERS, query }); const requests = []; @@ -51,7 +62,7 @@ export const loadHolders = projectKey => (dispatch, getState) => { return Promise.all(requests).then(responses => ( dispatch({ - type: 'RECEIVE_HOLDERS_SUCCESS', + type: RECEIVE_HOLDERS_SUCCESS, users: responses[0], groups: responses[1], query @@ -62,30 +73,30 @@ export const loadHolders = projectKey => (dispatch, getState) => { }; export const updateQuery = (projectKey, query = '') => dispatch => { - dispatch({ type: 'UPDATE_QUERY', query }); + dispatch({ type: UPDATE_QUERY, query }); if (query.length === 0 || query.length > 2) { dispatch(loadHolders(projectKey)); } }; export const updateFilter = (projectKey, filter) => dispatch => { - dispatch({ type: 'UPDATE_FILTER', filter }); + dispatch({ type: UPDATE_FILTER, filter }); dispatch(loadHolders(projectKey)); }; export const selectPermission = (projectKey, permission) => (dispatch, getState) => { - const selectedPermission = getSelectedPermission(getState()); + const selectedPermission = getPermissionsAppSelectedPermission(getState()); if (selectedPermission !== permission) { - dispatch({ type: 'SELECT_PERMISSION', permission }); + dispatch({ type: SELECT_PERMISSION, permission }); } else { - dispatch({ type: 'SELECT_PERMISSION', permission: null }); + dispatch({ type: SELECT_PERMISSION, permission: null }); } dispatch(loadHolders(projectKey)); }; export const grantToUser = (projectKey, login, permission) => dispatch => { api.grantPermissionToUser(projectKey, login, permission).then(() => { - dispatch({ type: 'GRANT_PERMISSION_TO_USER', login, permission }); + dispatch({ type: GRANT_PERMISSION_TO_USER, login, permission }); }).catch(e => { return parseError(e).then(message => dispatch(raiseError(message))); }); @@ -93,7 +104,7 @@ export const grantToUser = (projectKey, login, permission) => dispatch => { export const revokeFromUser = (projectKey, login, permission) => dispatch => { api.revokePermissionFromUser(projectKey, login, permission).then(() => { - dispatch({ type: 'REVOKE_PERMISSION_TO_USER', login, permission }); + dispatch({ type: REVOKE_PERMISSION_TO_USER, login, permission }); }).catch(e => { return parseError(e).then(message => dispatch(raiseError(message))); }); @@ -102,7 +113,7 @@ export const revokeFromUser = (projectKey, login, permission) => dispatch => { export const grantToGroup = (projectKey, groupName, permission) => dispatch => { api.grantPermissionToGroup(projectKey, groupName, permission).then(() => { dispatch({ - type: 'GRANT_PERMISSION_TO_GROUP', + type: GRANT_PERMISSION_TO_GROUP, groupName, permission }); @@ -114,7 +125,7 @@ export const grantToGroup = (projectKey, groupName, permission) => dispatch => { export const revokeFromGroup = (projectKey, groupName, permission) => dispatch => { api.revokePermissionFromGroup(projectKey, groupName, permission).then(() => { dispatch({ - type: 'REVOKE_PERMISSION_FROM_GROUP', + type: REVOKE_PERMISSION_FROM_GROUP, groupName, permission }); diff --git a/server/sonar-web/src/main/js/apps/permissions/routes.js b/server/sonar-web/src/main/js/apps/permissions/routes.js new file mode 100644 index 00000000000..a273a2914b6 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permissions/routes.js @@ -0,0 +1,31 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import GlobalPermissionsApp from './global/components/App'; +import ProjectPermissionsApp from './project/components/App'; + +export const globalPermissionsRoutes = ( + <IndexRoute component={GlobalPermissionsApp}/> +); + +export const projectPermissionsRoutes = ( + <IndexRoute component={ProjectPermissionsApp}/> +); diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/components/PageError.js b/server/sonar-web/src/main/js/apps/permissions/shared/components/PageError.js index 3449153a921..289a6c196a0 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/components/PageError.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/components/PageError.js @@ -19,7 +19,7 @@ */ import React from 'react'; import { connect } from 'react-redux'; -import { getError } from '../store/rootReducer'; +import { getPermissionsAppError } from '../../../../app/store/rootReducer'; class PageError extends React.Component { static propTypes = { @@ -42,7 +42,7 @@ class PageError extends React.Component { } const mapStateToProps = state => ({ - message: getError(state) + message: getPermissionsAppError(state) }); export default connect( diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/store/actions.js b/server/sonar-web/src/main/js/apps/permissions/shared/store/actions.js index be7cee4a4b7..ceed4f2f9ec 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/store/actions.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/store/actions.js @@ -17,7 +17,18 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +export const RECEIVE_HOLDERS_SUCCESS = 'permissions/RECEIVE_HOLDERS_SUCCESS'; +export const GRANT_PERMISSION_TO_GROUP = 'permissions/GRANT_PERMISSION_TO_GROUP'; +export const REVOKE_PERMISSION_FROM_GROUP = 'permissions/REVOKE_PERMISSION_FROM_GROUP'; +export const GRANT_PERMISSION_TO_USER = 'permissions/GRANT_PERMISSION_TO_USER'; +export const REVOKE_PERMISSION_TO_USER = 'permissions/REVOKE_PERMISSION_TO_USER'; +export const UPDATE_FILTER = 'permissions/UPDATE_FILTER'; +export const UPDATE_QUERY = 'permissions/UPDATE_QUERY'; +export const SELECT_PERMISSION = 'permissions/SELECT_PERMISSION'; +export const REQUEST_HOLDERS = 'permissions/REQUEST_HOLDERS'; +export const ERROR = 'permissions/ERROR'; + export const raiseError = message => ({ - type: 'ERROR', + type: ERROR, message }); diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/store/error.js b/server/sonar-web/src/main/js/apps/permissions/shared/store/error.js index 6fe0ec764c3..b56a9ee6cbf 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/store/error.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/store/error.js @@ -17,15 +17,24 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { + RECEIVE_HOLDERS_SUCCESS, + GRANT_PERMISSION_TO_USER, + REVOKE_PERMISSION_TO_USER, + GRANT_PERMISSION_TO_GROUP, + REVOKE_PERMISSION_FROM_GROUP, + ERROR +} from './actions'; + const error = (state = null, action = {}) => { switch (action.type) { - case 'RECEIVE_HOLDERS_SUCCESS': - case 'GRANT_PERMISSION_TO_USER': - case 'REVOKE_PERMISSION_TO_USER': - case 'GRANT_PERMISSION_TO_GROUP': - case 'REVOKE_PERMISSION_FROM_GROUP': + case RECEIVE_HOLDERS_SUCCESS: + case GRANT_PERMISSION_TO_USER: + case REVOKE_PERMISSION_TO_USER: + case GRANT_PERMISSION_TO_GROUP: + case REVOKE_PERMISSION_FROM_GROUP: return null; - case 'ERROR': + case ERROR: return action.message; default: return state; diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/store/filter.js b/server/sonar-web/src/main/js/apps/permissions/shared/store/filter.js index 6934582b425..65fa36db24d 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/store/filter.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/store/filter.js @@ -17,9 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { UPDATE_FILTER } from './actions'; + const filter = (state = 'all', action = {}) => { switch (action.type) { - case 'UPDATE_FILTER': + case UPDATE_FILTER: return action.filter; default: return state; diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/store/groups/byName.js b/server/sonar-web/src/main/js/apps/permissions/shared/store/groups/byName.js index c1475aa6763..a015820d0df 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/store/groups/byName.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/store/groups/byName.js @@ -18,16 +18,17 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import keyBy from 'lodash/keyBy'; +import { RECEIVE_HOLDERS_SUCCESS, GRANT_PERMISSION_TO_GROUP, REVOKE_PERMISSION_FROM_GROUP } from '../actions'; const byName = (state = {}, action = {}) => { - if (action.type === 'RECEIVE_HOLDERS_SUCCESS') { + if (action.type === RECEIVE_HOLDERS_SUCCESS) { const newGroups = keyBy(action.groups, 'name'); return { ...state, ...newGroups }; - } else if (action.type === 'GRANT_PERMISSION_TO_GROUP') { + } else if (action.type === GRANT_PERMISSION_TO_GROUP) { const newGroup = { ...state[action.groupName] }; newGroup.permissions = [...newGroup.permissions, action.permission]; return { ...state, [action.groupName]: newGroup }; - } else if (action.type === 'REVOKE_PERMISSION_FROM_GROUP') { + } else if (action.type === REVOKE_PERMISSION_FROM_GROUP) { const newGroup = { ...state[action.groupName] }; newGroup.permissions = newGroup.permissions .filter(p => p !== action.permission); diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/store/groups/names.js b/server/sonar-web/src/main/js/apps/permissions/shared/store/groups/names.js index 8451a26999e..4dbf7b34c8d 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/store/groups/names.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/store/groups/names.js @@ -17,8 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { RECEIVE_HOLDERS_SUCCESS } from '../actions'; + const names = (state = [], action = {}) => { - if (action.type === 'RECEIVE_HOLDERS_SUCCESS') { + if (action.type === RECEIVE_HOLDERS_SUCCESS) { return action.groups.map(group => group.name); } else { return state; diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/store/loading.js b/server/sonar-web/src/main/js/apps/permissions/shared/store/loading.js index b7ff6d172bc..87d7ee4d900 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/store/loading.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/store/loading.js @@ -17,11 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { REQUEST_HOLDERS, RECEIVE_HOLDERS_SUCCESS } from './actions'; + const loading = (state = false, action = {}) => { switch (action.type) { - case 'REQUEST_HOLDERS': + case REQUEST_HOLDERS: return true; - case 'RECEIVE_HOLDERS_SUCCESS': + case RECEIVE_HOLDERS_SUCCESS: return false; default: return state; diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/store/query.js b/server/sonar-web/src/main/js/apps/permissions/shared/store/query.js index 49a755a3fee..b8dbc36a794 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/store/query.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/store/query.js @@ -17,10 +17,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { UPDATE_QUERY, REQUEST_HOLDERS } from './actions'; + const query = (state = '', action = {}) => { switch (action.type) { - case 'UPDATE_QUERY': - case 'REQUEST_HOLDERS': + case UPDATE_QUERY: + case REQUEST_HOLDERS: return action.query; default: return state; diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/store/selectedPermission.js b/server/sonar-web/src/main/js/apps/permissions/shared/store/selectedPermission.js index c4e951745d7..b44531e3c12 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/store/selectedPermission.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/store/selectedPermission.js @@ -17,9 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { SELECT_PERMISSION } from './actions'; + const selectedPermission = (state = null, action = {}) => { switch (action.type) { - case 'SELECT_PERMISSION': + case SELECT_PERMISSION: return action.permission; default: return state; diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/store/users/byLogin.js b/server/sonar-web/src/main/js/apps/permissions/shared/store/users/byLogin.js index a571966cd92..fd3d21db256 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/store/users/byLogin.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/store/users/byLogin.js @@ -18,16 +18,17 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import keyBy from 'lodash/keyBy'; +import { RECEIVE_HOLDERS_SUCCESS, GRANT_PERMISSION_TO_USER, REVOKE_PERMISSION_TO_USER } from '../actions'; const byLogin = (state = {}, action = {}) => { - if (action.type === 'RECEIVE_HOLDERS_SUCCESS') { + if (action.type === RECEIVE_HOLDERS_SUCCESS) { const newUsers = keyBy(action.users, 'login'); return { ...state, ...newUsers }; - } else if (action.type === 'GRANT_PERMISSION_TO_USER') { + } else if (action.type === GRANT_PERMISSION_TO_USER) { const newUser = { ...state[action.login] }; newUser.permissions = [...newUser.permissions, action.permission]; return { ...state, [action.login]: newUser }; - } else if (action.type === 'REVOKE_PERMISSION_TO_USER') { + } else if (action.type === REVOKE_PERMISSION_TO_USER) { const newUser = { ...state[action.login] }; newUser.permissions = newUser.permissions .filter(p => p !== action.permission); diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/store/users/logins.js b/server/sonar-web/src/main/js/apps/permissions/shared/store/users/logins.js index b0bc1e31ca0..ad32bdc8573 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/store/users/logins.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/store/users/logins.js @@ -17,8 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { RECEIVE_HOLDERS_SUCCESS } from '../actions'; + const logins = (state = [], action = {}) => { - if (action.type === 'RECEIVE_HOLDERS_SUCCESS') { + if (action.type === RECEIVE_HOLDERS_SUCCESS) { return action.users.map(user => user.login); } else { return state; 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 deleted file mode 100644 index 903ed046cb9..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/app.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 { 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 QualityGate from './quality-gate/QualityGate'; -import Links from './links/Links'; -import Key from './key/Key'; -import rootReducer from './store/rootReducer'; -import configureStore from '../../components/store/configureStore'; - -window.sonarqube.appStarted.then(options => { - const el = document.querySelector(options.el); - - const history = useRouterHistory(createHistory)({ - basename: window.baseUrl + '/project' - }); - - const store = configureStore(rootReducer); - - const withComponent = ComposedComponent => props => - <ComposedComponent {...props} component={options.component}/>; - - render(( - <Provider store={store}> - <Router history={history}> - <Route - path="/deletion" - component={withComponent(Deletion)}/> - <Route - path="/quality_profiles" - component={withComponent(QualityProfiles)}/> - <Route - path="/quality_gate" - component={withComponent(QualityGate)}/> - <Route - path="/links" - component={withComponent(Links)}/> - <Route - path="/key" - component={withComponent(Key)}/> - </Router> - </Provider> - ), el); -}); diff --git a/server/sonar-web/src/main/js/apps/project-admin/components/GlobalMessagesContainer.js b/server/sonar-web/src/main/js/apps/project-admin/components/GlobalMessagesContainer.js index f64ef91664b..130192047b7 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/components/GlobalMessagesContainer.js +++ b/server/sonar-web/src/main/js/apps/project-admin/components/GlobalMessagesContainer.js @@ -19,10 +19,10 @@ */ import { connect } from 'react-redux'; import GlobalMessages from '../../../components/controls/GlobalMessages'; -import { getGlobalMessages } from '../store/rootReducer'; +import { getProjectAdminGlobalMessages } from '../../../app/store/rootReducer'; const mapStateToProps = state => ({ - messages: getGlobalMessages(state) + messages: getProjectAdminGlobalMessages(state) }); export default connect(mapStateToProps)(GlobalMessages); diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js b/server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js index 67563891ade..52e4fbc1774 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js +++ b/server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js @@ -23,10 +23,14 @@ import Form from './Form'; export default class Deletion extends React.Component { static propTypes = { - component: React.PropTypes.object.isRequired + component: React.PropTypes.object }; render () { + if (!this.props.component) { + return null; + } + return ( <div className="page page-limited"> <Header/> diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdate.js b/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdate.js index 315dc4b17f2..4bc8050a464 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdate.js +++ b/server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdate.js @@ -30,7 +30,7 @@ import { closeAllGlobalMessages } from '../../../components/store/globalMessages'; import { reloadUpdateKeyPage } from './utils'; -import RecentHistory from '../../../main/nav/component/RecentHistory'; +import RecentHistory from '../../../app/components/nav/component/RecentHistory'; class BulkUpdate extends React.Component { static propTypes = { diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/Key.js b/server/sonar-web/src/main/js/apps/project-admin/key/Key.js index 019cde640df..f4bff8b4a59 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/key/Key.js +++ b/server/sonar-web/src/main/js/apps/project-admin/key/Key.js @@ -25,7 +25,6 @@ import UpdateForm from './UpdateForm'; import BulkUpdate from './BulkUpdate'; import FineGrainedUpdate from './FineGrainedUpdate'; import GlobalMessagesContainer from '../components/GlobalMessagesContainer'; -import { getProjectModules } from '../store/rootReducer'; import { fetchProjectModules, changeKey } from '../store/actions'; import { translate } from '../../../helpers/l10n'; import { @@ -35,7 +34,8 @@ import { } from '../../../components/store/globalMessages'; import { parseError } from '../../code/utils'; import { reloadUpdateKeyPage } from './utils'; -import RecentHistory from '../../../main/nav/component/RecentHistory'; +import RecentHistory from '../../../app/components/nav/component/RecentHistory'; +import { getProjectAdminProjectModules } from '../../../app/store/rootReducer'; class Key extends React.Component { static propTypes = { @@ -152,7 +152,7 @@ class Key extends React.Component { } const mapStateToProps = (state, ownProps) => ({ - modules: getProjectModules(state, ownProps.component.key) + modules: getProjectAdminProjectModules(state, ownProps.component.key) }); export default connect( diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/Links.js b/server/sonar-web/src/main/js/apps/project-admin/links/Links.js index 88602532ea9..4494c52b5c9 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/links/Links.js +++ b/server/sonar-web/src/main/js/apps/project-admin/links/Links.js @@ -23,12 +23,12 @@ import { connect } from 'react-redux'; import Header from './Header'; import Table from './Table'; import DeletionModal from './views/DeletionModal'; -import { getProjectLinks } from '../store/rootReducer'; import { fetchProjectLinks, deleteProjectLink, createProjectLink } from '../store/actions'; +import { getProjectAdminProjectLinks } from '../../../app/store/rootReducer'; class Links extends React.Component { static propTypes = { @@ -73,7 +73,7 @@ class Links extends React.Component { } const mapStateToProps = (state, ownProps) => ({ - links: getProjectLinks(state, ownProps.component.key) + links: getProjectAdminProjectLinks(state, ownProps.component.key) }); export default connect( diff --git a/server/sonar-web/src/main/js/apps/project-admin/quality-gate/QualityGate.js b/server/sonar-web/src/main/js/apps/project-admin/quality-gate/QualityGate.js index e80c5b72284..0921f1f2060 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/quality-gate/QualityGate.js +++ b/server/sonar-web/src/main/js/apps/project-admin/quality-gate/QualityGate.js @@ -23,8 +23,8 @@ import shallowCompare from 'react-addons-shallow-compare'; import Header from './Header'; import Form from './Form'; import GlobalMessagesContainer from '../components/GlobalMessagesContainer'; -import { getAllGates, getProjectGate } from '../store/rootReducer'; import { fetchProjectGate, setProjectGate } from '../store/actions'; +import { getProjectAdminAllGates, getProjectAdminProjectGate } from '../../../app/store/rootReducer'; class QualityGate extends React.Component { static propTypes = { @@ -60,8 +60,8 @@ class QualityGate extends React.Component { } const mapStateToProps = (state, ownProps) => ({ - allGates: getAllGates(state), - gate: getProjectGate(state, ownProps.component.key) + allGates: getProjectAdminAllGates(state), + gate: getProjectAdminProjectGate(state, ownProps.component.key) }); export default connect( 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 index de20aa1ad8d..9bfdab5c533 100644 --- 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 @@ -24,7 +24,7 @@ import Header from './Header'; import Table from './Table'; import GlobalMessagesContainer from '../components/GlobalMessagesContainer'; import { fetchProjectProfiles, setProjectProfile } from '../store/actions'; -import { getProjectProfiles, getAllProfiles } from '../store/rootReducer'; +import { getProjectAdminAllProfiles, getProjectAdminProjectProfiles } from '../../../app/store/rootReducer'; class QualityProfiles extends React.Component { static propTypes = { @@ -68,8 +68,8 @@ class QualityProfiles extends React.Component { } const mapStateToProps = (state, ownProps) => ({ - allProfiles: getAllProfiles(state), - profiles: getProjectProfiles(state, ownProps.component.key) + allProfiles: getProjectAdminAllProfiles(state), + profiles: getProjectAdminProjectProfiles(state, ownProps.component.key) }); export default connect( diff --git a/server/sonar-web/src/main/js/apps/projects-admin/app.js b/server/sonar-web/src/main/js/apps/project-admin/routes.js index 7c68d2c4b6e..fda733c0002 100644 --- a/server/sonar-web/src/main/js/apps/projects-admin/app.js +++ b/server/sonar-web/src/main/js/apps/project-admin/routes.js @@ -18,16 +18,17 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import ReactDOM from 'react-dom'; -import Main from './main'; -import { getCurrentUser } from '../../api/users'; +import { Route } from 'react-router'; +import Deletion from './deletion/Deletion'; +import QualityProfiles from './quality-profiles/QualityProfiles'; +import QualityGate from './quality-gate/QualityGate'; +import Links from './links/Links'; +import Key from './key/Key'; -window.sonarqube.appStarted.then(options => { - getCurrentUser().then(user => { - const el = document.querySelector(options.el); - const hasProvisionPermission = user.permissions.global.indexOf('provisioning') !== -1; - const topLevelQualifiers = options.rootQualifiers; - ReactDOM.render(<Main hasProvisionPermission={hasProvisionPermission} - topLevelQualifiers={topLevelQualifiers}/>, el); - }); -}); +export default [ + <Route key="deletion" path="deletion" component={Deletion}/>, + <Route key="quality_profiles" path="quality_profiles" component={QualityProfiles}/>, + <Route key="quality_gate" path="quality_gate" component={QualityGate}/>, + <Route key="links" path="links" component={Links}/>, + <Route key="key" path="key" component={Key}/> +]; 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 index 18eb9917862..b5c3c1a5f21 100644 --- 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 @@ -18,7 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { getQualityProfiles, associateProject, dissociateProject } from '../../../api/quality-profiles'; -import { getProfileByKey } from './rootReducer'; import { fetchQualityGates, getGateForProject, @@ -29,14 +28,15 @@ import { getProjectLinks, createLink } from '../../../api/projectLinks'; import { getTree, changeKey as changeKeyApi } from '../../../api/components'; import { addGlobalSuccessMessage } from '../../../components/store/globalMessages'; import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { getProjectAdminProfileByKey } from '../../../app/store/rootReducer'; -export const RECEIVE_PROFILES = 'RECEIVE_PROFILES'; +export const RECEIVE_PROFILES = 'projectAdmin/RECEIVE_PROFILES'; export const receiveProfiles = profiles => ({ type: RECEIVE_PROFILES, profiles }); -export const RECEIVE_PROJECT_PROFILES = 'RECEIVE_PROJECT_PROFILES'; +export const RECEIVE_PROJECT_PROFILES = 'projectAdmin/RECEIVE_PROJECT_PROFILES'; export const receiveProjectProfiles = (projectKey, profiles) => ({ type: RECEIVE_PROJECT_PROFILES, projectKey, @@ -54,7 +54,7 @@ export const fetchProjectProfiles = projectKey => dispatch => { }); }; -export const SET_PROJECT_PROFILE = 'SET_PROJECT_PROFILE'; +export const SET_PROJECT_PROFILE = 'projectAdmin/SET_PROJECT_PROFILE'; const setProjectProfileAction = (projectKey, oldProfileKey, newProfileKey) => ({ type: SET_PROJECT_PROFILE, projectKey, @@ -65,7 +65,7 @@ const setProjectProfileAction = (projectKey, oldProfileKey, newProfileKey) => ({ export const setProjectProfile = (projectKey, oldKey, newKey) => (dispatch, getState) => { const state = getState(); - const newProfile = getProfileByKey(state, newKey); + const newProfile = getProjectAdminProfileByKey(state, newKey); const request = newProfile.isDefault ? dissociateProject(oldKey, projectKey) : associateProject(newKey, projectKey); @@ -79,13 +79,13 @@ export const setProjectProfile = (projectKey, oldKey, newKey) => }); }; -export const RECEIVE_GATES = 'RECEIVE_GATES'; +export const RECEIVE_GATES = 'projectAdmin/RECEIVE_GATES'; export const receiveGates = gates => ({ type: RECEIVE_GATES, gates }); -export const RECEIVE_PROJECT_GATE = 'RECEIVE_PROJECT_GATE'; +export const RECEIVE_PROJECT_GATE = 'projectAdmin/RECEIVE_PROJECT_GATE'; export const receiveProjectGate = (projectKey, gate) => ({ type: RECEIVE_PROJECT_GATE, projectKey, @@ -103,7 +103,7 @@ export const fetchProjectGate = projectKey => dispatch => { }); }; -export const SET_PROJECT_GATE = 'SET_PROJECT_GATE'; +export const SET_PROJECT_GATE = 'projectAdmin/SET_PROJECT_GATE'; const setProjectGateAction = (projectKey, gateId) => ({ type: SET_PROJECT_GATE, projectKey, @@ -122,7 +122,7 @@ export const setProjectGate = (projectKey, oldId, newId) => dispatch => { }); }; -export const RECEIVE_PROJECT_LINKS = 'RECEIVE_PROJECT_LINKS'; +export const RECEIVE_PROJECT_LINKS = 'projectAdmin/RECEIVE_PROJECT_LINKS'; export const receiveProjectLinks = (projectKey, links) => ({ type: RECEIVE_PROJECT_LINKS, projectKey, @@ -135,7 +135,7 @@ export const fetchProjectLinks = projectKey => dispatch => { }); }; -export const ADD_PROJECT_LINK = 'ADD_PROJECT_LINK'; +export const ADD_PROJECT_LINK = 'projectAdmin/ADD_PROJECT_LINK'; const addProjectLink = (projectKey, link) => ({ type: ADD_PROJECT_LINK, projectKey, @@ -148,14 +148,14 @@ export const createProjectLink = (projectKey, name, url) => dispatch => { }); }; -export const DELETE_PROJECT_LINK = 'DELETE_PROJECT_LINK'; +export const DELETE_PROJECT_LINK = 'projectAdmin/DELETE_PROJECT_LINK'; export const deleteProjectLink = (projectKey, linkId) => ({ type: DELETE_PROJECT_LINK, projectKey, linkId }); -export const RECEIVE_PROJECT_MODULES = 'RECEIVE_PROJECT_MODULES'; +export const RECEIVE_PROJECT_MODULES = 'projectAdmin/RECEIVE_PROJECT_MODULES'; const receiveProjectModules = (projectKey, modules) => ({ type: RECEIVE_PROJECT_MODULES, projectKey, @@ -169,7 +169,7 @@ export const fetchProjectModules = projectKey => dispatch => { }); }; -export const CHANGE_KEY = 'CHANGE_KEY'; +export const CHANGE_KEY = 'projectAdmin/CHANGE_KEY'; const changeKeyAction = (key, newKey) => ({ type: CHANGE_KEY, key, diff --git a/server/sonar-web/src/main/js/apps/background-tasks/app.js b/server/sonar-web/src/main/js/apps/projects-admin/AppContainer.js index 849bd897cb7..e87d63f8ec6 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/app.js +++ b/server/sonar-web/src/main/js/apps/projects-admin/AppContainer.js @@ -17,27 +17,39 @@ * 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 { connect } from 'react-redux'; import React from 'react'; -import ReactDOM from 'react-dom'; -import { Router, Route, Redirect, useRouterHistory } from 'react-router'; -import { createHistory } from 'history'; +import Main from './main'; +import { getCurrentUser } from '../../app/store/rootReducer'; -import BackgroundTasksApp from './components/BackgroundTasksApp'; +class AppContainer extends React.Component { + state = {}; -window.sonarqube.appStarted.then(options => { - const el = document.querySelector(options.el); + componentDidMount () { + window.sonarqube.appStarted.then(options => { + this.setState({ rootQualifiers: options.rootQualifiers }); + }); + } - const history = useRouterHistory(createHistory)({ - basename: window.baseUrl + (options.component ? '/project/background_tasks' : '/background_tasks') - }); + render () { + if (!this.props.user || !this.state.rootQualifiers) { + return null; + } - const App = props => <BackgroundTasksApp {...props} component={options.component}/>; + const hasProvisionPermission = this.props.user.permissions.global.indexOf('provisioning') !== -1; - ReactDOM.render(( - <Router history={history}> - <Redirect from="/index" to="/"/> - <Route path="/" component={App}/> - </Router> - ), el); + return ( + <Main + hasProvisionPermission={hasProvisionPermission} + topLevelQualifiers={this.state.rootQualifiers}/> + ); + } +} + +const mapStateToProps = state => ({ + user: getCurrentUser(state) }); + +export default connect( + mapStateToProps +)(AppContainer); diff --git a/server/sonar-web/src/main/js/apps/projects-admin/routes.js b/server/sonar-web/src/main/js/apps/projects-admin/routes.js new file mode 100644 index 00000000000..5b25254fb7f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects-admin/routes.js @@ -0,0 +1,26 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import AppContainer from './AppContainer'; + +export default( + <IndexRoute component={AppContainer}/> +); diff --git a/server/sonar-web/src/main/js/apps/projects/routes.js b/server/sonar-web/src/main/js/apps/projects/routes.js index 114e82ca197..017baac85be 100644 --- a/server/sonar-web/src/main/js/apps/projects/routes.js +++ b/server/sonar-web/src/main/js/apps/projects/routes.js @@ -24,7 +24,7 @@ import AllProjectsContainer from './components/AllProjectsContainer'; import FavoriteProjectsContainer from './components/FavoriteProjectsContainer'; export default ( - <Route path="projects" component={App}> + <Route component={App}> <IndexRoute component={AllProjectsContainer}/> <Route path="favorite" component={FavoriteProjectsContainer}/> </Route> diff --git a/server/sonar-web/src/main/js/apps/quality-gates/routes.js b/server/sonar-web/src/main/js/apps/quality-gates/routes.js index 8f471c3930b..1a05f4e4657 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/routes.js +++ b/server/sonar-web/src/main/js/apps/quality-gates/routes.js @@ -24,7 +24,7 @@ import Intro from './components/Intro'; import DetailsContainer from './containers/DetailsContainer'; export default ( - <Route path="quality_gates" component={QualityGatesAppContainer}> + <Route component={QualityGatesAppContainer}> <Redirect from="/quality_gates/index" to="/quality_gates/"/> <IndexRoute component={Intro}/> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/routes.js b/server/sonar-web/src/main/js/apps/quality-profiles/routes.js index b5729b5643b..3530edc7a91 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/routes.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/routes.js @@ -27,7 +27,7 @@ import ChangelogContainer from './changelog/ChangelogContainer'; import ComparisonContainer from './compare/ComparisonContainer'; export default ( - <Route path="profiles" component={AppContainer}> + <Route component={AppContainer}> <Redirect from="/profiles/index" to="/profiles/"/> <IndexRoute component={HomeContainer}/> diff --git a/server/sonar-web/src/main/js/apps/settings/app.js b/server/sonar-web/src/main/js/apps/settings/app.js deleted file mode 100644 index 15040500397..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/app.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 { render } from 'react-dom'; -import { Provider } from 'react-redux'; -import { Router, Route, Redirect, useRouterHistory } from 'react-router'; -import { createHistory } from 'history'; -import App from './components/App'; -import LicensesApp from './licenses/LicensesApp'; -import EncryptionAppContainer from './encryption/EncryptionAppContainer'; -import ServerIdAppContainer from './serverId/ServerIdAppContainer'; -import rootReducer from './store/rootReducer'; -import configureStore from '../../components/store/configureStore'; - -window.sonarqube.appStarted.then(options => { - const el = document.querySelector(options.el); - - const controller = options.component ? '/project/settings' : '/settings'; - const history = useRouterHistory(createHistory)({ - basename: window.baseUrl + controller - }); - - const store = configureStore(rootReducer); - - const withComponent = ComposedComponent => props => - <ComposedComponent {...props} component={options.component}/>; - - render(( - <Provider store={store}> - <Router history={history}> - <Redirect from="/index" to="/"/> - <Route path="/" component={withComponent(App)}/> - <Route path="/licenses" component={LicensesApp}/> - <Route path="/encryption" component={EncryptionAppContainer}/> - <Route path="/server_id" component={ServerIdAppContainer}/> - </Router> - </Provider> - ), el); -}); diff --git a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.js b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.js index 285f9e769e7..96fccd0abfe 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.js +++ b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.js @@ -21,7 +21,7 @@ import React from 'react'; import { connect } from 'react-redux'; import CategoriesList from './CategoriesList'; -import { getAllCategories } from '../store/rootReducer'; +import { getSettingsAppAllCategories } from '../../../app/store/rootReducer'; class AllCategoriesList extends React.Component { render () { @@ -30,7 +30,7 @@ class AllCategoriesList extends React.Component { } const mapStateToProps = state => ({ - categories: getAllCategories(state) + categories: getSettingsAppAllCategories(state) }); export default connect( diff --git a/server/sonar-web/src/main/js/apps/settings/components/App.js b/server/sonar-web/src/main/js/apps/settings/components/App.js index 9f2c99ecb6b..902e4bf4602 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/App.js +++ b/server/sonar-web/src/main/js/apps/settings/components/App.js @@ -27,7 +27,7 @@ import AllCategoriesList from './AllCategoriesList'; import GlobalMessagesContainer from './GlobalMessagesContainer'; import WildcardsHelp from './WildcardsHelp'; import { fetchSettings } from '../store/actions'; -import { getDefaultCategory } from '../store/rootReducer'; +import { getSettingsAppDefaultCategory } from '../../../app/store/rootReducer'; import '../styles.css'; type Props = { @@ -103,7 +103,7 @@ class App extends React.Component { } const mapStateToProps = state => ({ - defaultCategory: getDefaultCategory(state) + defaultCategory: getSettingsAppDefaultCategory(state) }); export default connect( diff --git a/server/sonar-web/src/main/js/apps/settings/components/AppContainer.js b/server/sonar-web/src/main/js/apps/settings/components/AppContainer.js new file mode 100644 index 00000000000..e4cd869edf9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/AppContainer.js @@ -0,0 +1,40 @@ +/* + * 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 App from './App'; + +export default class AppContainer extends React.Component { + state = {}; + + componentDidMount () { + window.sonarqube.appStarted.then(options => + this.setState({ ready: true, component: options.component })); + } + + render () { + if (!this.state.ready) { + return null; + } + + return ( + <App {...this.props} component={this.state.component}/> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/CategoriesList.js b/server/sonar-web/src/main/js/apps/settings/components/CategoriesList.js index 5fb235d1da6..03b8f66ba75 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/CategoriesList.js +++ b/server/sonar-web/src/main/js/apps/settings/components/CategoriesList.js @@ -37,7 +37,7 @@ type Props = { }; export default class CategoriesList extends React.Component { - props: Props; + rops: Props; shouldComponentUpdate (nextProps: Props, nextState: ?{}) { return shallowCompare(this, nextProps, nextState); @@ -56,8 +56,10 @@ export default class CategoriesList extends React.Component { const className = category.key.toLowerCase() === this.props.selectedCategory.toLowerCase() ? 'active' : ''; + const pathname = this.props.component ? '/project/settings' : '/settings'; + return ( - <IndexLink to={{ pathname: '/', query }} className={className} title={category.name}> + <IndexLink to={{ pathname, query }} className={className} title={category.name}> {category.name} </IndexLink> ); diff --git a/server/sonar-web/src/main/js/apps/settings/components/CategoryDefinitionsList.js b/server/sonar-web/src/main/js/apps/settings/components/CategoryDefinitionsList.js index 8d7bf30fc1d..510cffb443a 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/CategoryDefinitionsList.js +++ b/server/sonar-web/src/main/js/apps/settings/components/CategoryDefinitionsList.js @@ -21,7 +21,7 @@ import React from 'react'; import { connect } from 'react-redux'; import SubCategoryDefinitionsList from './SubCategoryDefinitionsList'; -import { getSettingsForCategory } from '../store/rootReducer'; +import { getSettingsAppSettingsForCategory } from '../../../app/store/rootReducer'; class CategoryDefinitionsList extends React.Component { @@ -31,7 +31,7 @@ class CategoryDefinitionsList extends React.Component { } const mapStateToProps = (state, ownProps) => ({ - settings: getSettingsForCategory(state, ownProps.category) + settings: getSettingsAppSettingsForCategory(state, ownProps.category) }); export default connect( diff --git a/server/sonar-web/src/main/js/apps/settings/components/Definition.js b/server/sonar-web/src/main/js/apps/settings/components/Definition.js index 065b6b447f4..0aa95078681 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/Definition.js +++ b/server/sonar-web/src/main/js/apps/settings/components/Definition.js @@ -28,10 +28,14 @@ import DefinitionChanges from './DefinitionChanges'; import { getPropertyName, getPropertyDescription, getSettingValue, isDefaultOrInherited } from '../utils'; import { translateWithParameters, translate } from '../../../helpers/l10n'; import { resetValue, saveValue } from '../store/actions'; -import { isLoading, getValidationMessage, getChangedValue } from '../store/rootReducer'; import { failValidation, passValidation } from '../store/settingsPage/validationMessages/actions'; import { cancelChange, changeValue } from '../store/settingsPage/changedValues/actions'; import { TYPE_PASSWORD } from '../constants'; +import { + getSettingsAppChangedValue, + isSettingsAppLoading, + getSettingsAppValidationMessage +} from '../../../app/store/rootReducer'; class Definition extends React.Component { mounted: boolean; @@ -186,9 +190,9 @@ class Definition extends React.Component { } const mapStateToProps = (state, ownProps) => ({ - changedValue: getChangedValue(state, ownProps.setting.definition.key), - loading: isLoading(state, ownProps.setting.definition.key), - validationMessage: getValidationMessage(state, ownProps.setting.definition.key) + changedValue: getSettingsAppChangedValue(state, ownProps.setting.definition.key), + loading: isSettingsAppLoading(state, ownProps.setting.definition.key), + validationMessage: getSettingsAppValidationMessage(state, ownProps.setting.definition.key) }); export default connect( diff --git a/server/sonar-web/src/main/js/apps/settings/components/GlobalMessagesContainer.js b/server/sonar-web/src/main/js/apps/settings/components/GlobalMessagesContainer.js index da821b7d65f..9c9af651c6a 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/GlobalMessagesContainer.js +++ b/server/sonar-web/src/main/js/apps/settings/components/GlobalMessagesContainer.js @@ -20,10 +20,10 @@ // @flow import { connect } from 'react-redux'; import GlobalMessages from '../../../components/controls/GlobalMessages'; -import { getGlobalMessages } from '../store/rootReducer'; +import { getSettingsAppGlobalMessages } from '../../../app/store/rootReducer'; const mapStateToProps = state => ({ - messages: getGlobalMessages(state) + messages: getSettingsAppGlobalMessages(state) }); export default connect(mapStateToProps)(GlobalMessages); diff --git a/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionAppContainer.js b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionAppContainer.js index 42bd05c788d..0dd8e051b67 100644 --- a/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionAppContainer.js +++ b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionAppContainer.js @@ -20,9 +20,9 @@ import { connect } from 'react-redux'; import EncryptionApp from './EncryptionApp'; import { checkSecretKey, generateSecretKey, encryptValue, startGeneration } from '../store/encryptionPage/actions'; -import { getEncryptionState } from '../store/rootReducer'; +import { getSettingsAppEncryptionState } from '../../../app/store/rootReducer'; export default connect( - state => getEncryptionState(state), + state => getSettingsAppEncryptionState(state), { checkSecretKey, generateSecretKey, encryptValue, startGeneration } )(EncryptionApp); diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRowContainer.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRowContainer.js index af004df569f..0b8fefd5c39 100644 --- a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRowContainer.js +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRowContainer.js @@ -19,11 +19,11 @@ */ import { connect } from 'react-redux'; import LicenseRow from './LicenseRow'; -import { getLicenseByKey } from '../store/rootReducer'; import { setLicense } from '../store/licenses/actions'; +import { getSettingsAppLicenseByKey } from '../../../app/store/rootReducer'; const mapStateToProps = (state, ownProps) => ({ - license: getLicenseByKey(state, ownProps.licenseKey) + license: getSettingsAppLicenseByKey(state, ownProps.licenseKey) }); export default connect( diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicensesListContainer.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicensesListContainer.js index bd692b7e390..ca19e0a2b41 100644 --- a/server/sonar-web/src/main/js/apps/settings/licenses/LicensesListContainer.js +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicensesListContainer.js @@ -20,10 +20,10 @@ import { connect } from 'react-redux'; import LicensesList from './LicensesList'; import { fetchLicenses } from '../store/licenses/actions'; -import { getAllLicenseKeys } from '../store/rootReducer'; +import { getSettingsAppAllLicenseKeys } from '../../../app/store/rootReducer'; const mapStateToProps = state => ({ - licenses: getAllLicenseKeys(state) + licenses: getSettingsAppAllLicenseKeys(state) }); export default connect( diff --git a/server/sonar-web/src/main/js/apps/permission-templates/app.js b/server/sonar-web/src/main/js/apps/settings/routes.js index 036918421e7..1982da9b7aa 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/app.js +++ b/server/sonar-web/src/main/js/apps/settings/routes.js @@ -18,27 +18,16 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import { render } from 'react-dom'; -import { Router, Route, useRouterHistory } from 'react-router'; -import { createHistory } from 'history'; -import App from './components/App'; +import { IndexRoute, Route, Redirect } from 'react-router'; +import AppContainer from './components/AppContainer'; +import LicensesApp from './licenses/LicensesApp'; +import EncryptionAppContainer from './encryption/EncryptionAppContainer'; +import ServerIdAppContainer from './serverId/ServerIdAppContainer'; -window.sonarqube.appStarted.then(options => { - const el = document.querySelector(options.el); - - const history = useRouterHistory(createHistory)({ - basename: window.baseUrl + '/permission_templates' - }); - - const EnhancedApp = props => ( - <App - {...props} - topQualifiers={options.rootQualifiers}/> - ); - - render(( - <Router history={history}> - <Route path="/" component={EnhancedApp}/> - </Router> - ), el); -}); +export default [ + <Redirect key="1" from="/settings/index" to="/settings"/>, + <IndexRoute key="2" component={AppContainer}/>, + <Route key="3" path="licenses" component={LicensesApp}/>, + <Route key="4" path="encryption" component={EncryptionAppContainer}/>, + <Route key="5" path="server_id" component={ServerIdAppContainer}/> +]; diff --git a/server/sonar-web/src/main/js/apps/settings/store/actions.js b/server/sonar-web/src/main/js/apps/settings/store/actions.js index df6ed194049..79f4d15ed9c 100644 --- a/server/sonar-web/src/main/js/apps/settings/store/actions.js +++ b/server/sonar-web/src/main/js/apps/settings/store/actions.js @@ -25,9 +25,9 @@ import { parseError } from '../../code/utils'; import { addGlobalErrorMessage, closeAllGlobalMessages } from '../../../components/store/globalMessages'; import { passValidation, failValidation } from './settingsPage/validationMessages/actions'; import { cancelChange } from './settingsPage/changedValues/actions'; -import { getDefinition, getChangedValue } from './rootReducer'; import { isEmptyValue } from '../utils'; import { translate } from '../../../helpers/l10n'; +import { getSettingsAppDefinition, getSettingsAppChangedValue } from '../../../app/store/rootReducer'; export const fetchSettings = componentKey => dispatch => { return getDefinitions(componentKey) @@ -48,8 +48,8 @@ export const saveValue = (key, componentKey) => (dispatch, getState) => { dispatch(startLoading(key)); const state = getState(); - const definition = getDefinition(state, key); - const value = getChangedValue(state, key); + const definition = getSettingsAppDefinition(state, key); + const value = getSettingsAppChangedValue(state, key); if (isEmptyValue(definition, value)) { dispatch(failValidation(key, translate('settings.state.value_cant_be_empty'))); diff --git a/server/sonar-web/src/main/js/apps/system/app.js b/server/sonar-web/src/main/js/apps/system/routes.js index 652fd6d06bc..9d257927d7f 100644 --- a/server/sonar-web/src/main/js/apps/system/app.js +++ b/server/sonar-web/src/main/js/apps/system/routes.js @@ -18,11 +18,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import ReactDOM from 'react-dom'; +import { IndexRoute, Redirect } from 'react-router'; import Main from './main'; -window.sonarqube.appStarted.then(options => { - const el = document.querySelector(options.el); - ReactDOM.render(<Main/>, el); -}); - +export default [ + <Redirect key="redirect" from="/system/index" to="/system"/>, + <IndexRoute key="index" component={Main}/> +]; diff --git a/server/sonar-web/src/main/js/apps/update-center/components/UpdateCenterAppContainer.js b/server/sonar-web/src/main/js/apps/update-center/components/UpdateCenterAppContainer.js new file mode 100644 index 00000000000..4e40999fe81 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/update-center/components/UpdateCenterAppContainer.js @@ -0,0 +1,31 @@ +/* + * 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 init from '../init'; + +export default class UpdateCenterAppContainer extends React.Component { + componentDidMount () { + init(this.refs.container); + } + + render () { + return <div ref="container"/>; + } +} diff --git a/server/sonar-web/src/main/js/apps/update-center/app.js b/server/sonar-web/src/main/js/apps/update-center/init.js index 3cf3adc9d38..3c509e36232 100644 --- a/server/sonar-web/src/main/js/apps/update-center/app.js +++ b/server/sonar-web/src/main/js/apps/update-center/init.js @@ -29,16 +29,14 @@ import Router from './router'; import Plugins from './plugins'; const App = new Marionette.Application(); -const init = function () { - const options = window.sonarqube; - +const init = function (el) { // State this.state = new Backbone.Model({ updateCenterActive: window.SS.updateCenterActive }); // Layout - this.layout = new Layout({ el: options.el }); + this.layout = new Layout({ el }); this.layout.render(); // Plugins @@ -70,13 +68,14 @@ const init = function () { // Go Backbone.history.start({ pushState: true, - root: options.urlRoot + root: window.baseUrl + '/updatecenter' }); }; -App.on('start', function () { - init.call(App); +App.on('start', function (el) { + init.call(App, el); }); -window.sonarqube.appStarted.then(options => App.start(options)); - +export default function (el) { + App.start(el); +} diff --git a/server/sonar-web/src/main/js/apps/permissions/project/app.js b/server/sonar-web/src/main/js/apps/update-center/routes.js index 1646df7ef24..308a3fa8538 100644 --- a/server/sonar-web/src/main/js/apps/permissions/project/app.js +++ b/server/sonar-web/src/main/js/apps/update-center/routes.js @@ -18,18 +18,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import { render } from 'react-dom'; -import { Provider } from 'react-redux'; -import App from './components/App'; -import configureStore from '../../../components/store/configureStore'; -import rootReducer from '../shared/store/rootReducer'; +import { IndexRoute, Route } from 'react-router'; +import UpdateCenterAppContainer from './components/UpdateCenterAppContainer'; -window.sonarqube.appStarted.then(options => { - const el = document.querySelector(options.el); - const store = configureStore(rootReducer); - render(( - <Provider store={store}> - <App project={options.component}/> - </Provider> - ), el); -}); +export default [ + <IndexRoute key="index" component={UpdateCenterAppContainer}/>, + <Route key="installed" path="installed" component={UpdateCenterAppContainer}/>, + <Route key="updates" path="updates" component={UpdateCenterAppContainer}/>, + <Route key="available" path="available" component={UpdateCenterAppContainer}/>, + <Route key="system" path="system" component={UpdateCenterAppContainer}/> +]; diff --git a/server/sonar-web/src/main/js/apps/users/components/UsersAppContainer.js b/server/sonar-web/src/main/js/apps/users/components/UsersAppContainer.js new file mode 100644 index 00000000000..8442a308327 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/users/components/UsersAppContainer.js @@ -0,0 +1,31 @@ +/* + * 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 init from '../init'; + +export default class UsersAppContainer extends React.Component { + componentDidMount () { + init(this.refs.container); + } + + render () { + return <div ref="container"/>; + } +} diff --git a/server/sonar-web/src/main/js/apps/users/app.js b/server/sonar-web/src/main/js/apps/users/init.js index 14ea47e63e3..cd1dc8a6568 100644 --- a/server/sonar-web/src/main/js/apps/users/app.js +++ b/server/sonar-web/src/main/js/apps/users/init.js @@ -29,11 +29,9 @@ import { getIdentityProviders } from '../../api/users'; const App = new Marionette.Application(); -const init = function (providers) { - const options = window.sonarqube; - +const init = function (el, providers) { // Layout - this.layout = new Layout({ el: options.el }); + this.layout = new Layout({ el }); this.layout.render(); // Collection @@ -59,9 +57,10 @@ const init = function (providers) { this.users.fetch(); }; -App.on('start', function () { - getIdentityProviders().then(r => init.call(App, r.identityProviders)); +App.on('start', function (el) { + getIdentityProviders().then(r => init.call(App, el, r.identityProviders)); }); -window.sonarqube.appStarted.then(options => App.start(options)); - +export default function (el) { + App.start(el); +} diff --git a/server/sonar-web/src/main/js/apps/users/routes.js b/server/sonar-web/src/main/js/apps/users/routes.js new file mode 100644 index 00000000000..b727131f339 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/users/routes.js @@ -0,0 +1,26 @@ +/* + * 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 { IndexRoute } from 'react-router'; +import UsersAppContainer from './components/UsersAppContainer'; + +export default ( + <IndexRoute component={UsersAppContainer}/> +); diff --git a/server/sonar-web/src/main/js/apps/web-api/app.js b/server/sonar-web/src/main/js/apps/web-api/app.js deleted file mode 100644 index 0ff00d28a30..00000000000 --- a/server/sonar-web/src/main/js/apps/web-api/app.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 { render } from 'react-dom'; -import { Router, Route, Redirect, useRouterHistory } from 'react-router'; -import { createHistory } from 'history'; - -import WebApiApp from './components/WebApiApp'; -import './styles/web-api.css'; - -window.sonarqube.appStarted.then(options => { - const el = document.querySelector(options.el); - - const history = useRouterHistory(createHistory)({ - basename: window.sonarqube.urlRoot - }); - - render(( - <Router history={history}> - <Redirect from="/index" to="/"/> - <Route path="/**" component={WebApiApp}/> - </Router> - ), el); -}); diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Action.js b/server/sonar-web/src/main/js/apps/web-api/components/Action.js index 2c3cc0dcfa2..cfe3a6df465 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/Action.js +++ b/server/sonar-web/src/main/js/apps/web-api/components/Action.js @@ -60,7 +60,7 @@ export default class Action extends React.Component { <TooltipsContainer> <header className="web-api-action-header"> <Link - to={{ pathname: '/' + actionKey }} + to={{ pathname: '/web_api/' + actionKey }} className="spacer-right icon-link"/> <h3 className="web-api-action-title"> diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Menu.js b/server/sonar-web/src/main/js/apps/web-api/components/Menu.js index af81e50fb45..198fd6cc5f3 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/Menu.js +++ b/server/sonar-web/src/main/js/apps/web-api/components/Menu.js @@ -50,7 +50,7 @@ export default function Menu ({ domains, showInternal, showOnlyDeprecated, searc <Link key={domain.path} className={classNames('list-group-item', { 'active': isDomainPathActive(domain.path, splat) })} - to={domain.path}> + to={'/web_api/' + domain.path}> <h3 className="list-group-item-heading"> {domain.path} {domain.internal && ( diff --git a/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js index 51c56ab4839..ab5dfebbf23 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js +++ b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js @@ -24,6 +24,7 @@ import Menu from './Menu'; import Search from './Search'; import Domain from './Domain'; import { getActionKey, isDomainPathActive } from '../utils'; +import '../styles/web-api.css'; export default class WebApiApp extends React.Component { state = { @@ -59,7 +60,7 @@ export default class WebApiApp extends React.Component { } scrollToAction () { - const { splat } = this.props.params; + const splat = this.props.params.splat || ''; this.scrollToElement(splat); } @@ -77,7 +78,7 @@ export default class WebApiApp extends React.Component { } toggleInternalInitially () { - const { splat } = this.props.params; + const splat = this.props.params.splat || ''; const { domains, showInternal } = this.state; if (!showInternal) { @@ -100,7 +101,7 @@ export default class WebApiApp extends React.Component { } handleToggleInternal () { - const { splat } = this.props.params; + const splat = this.props.params.splat || ''; const { router } = this.context; const { domains } = this.state; const domain = domains.find(domain => isDomainPathActive(domain.path, splat)); @@ -118,7 +119,7 @@ export default class WebApiApp extends React.Component { } render () { - const { splat } = this.props.params; + const splat = this.props.params.splat || ''; const { domains, showInternal, showOnlyDeprecated, searchQuery } = this.state; const domain = domains.find(domain => isDomainPathActive(domain.path, splat)); @@ -127,7 +128,7 @@ export default class WebApiApp extends React.Component { <div className="search-navigator sticky"> <div className="search-navigator-side search-navigator-side-light" style={{ top: 30 }}> <div className="web-api-page-header"> - <Link to="/"> + <Link to="/web_api/"> <h1>Web API</h1> </Link> </div> diff --git a/server/sonar-web/src/main/js/apps/web-api/routes.js b/server/sonar-web/src/main/js/apps/web-api/routes.js new file mode 100644 index 00000000000..18daa8ccd71 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/web-api/routes.js @@ -0,0 +1,27 @@ +/* + * 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 { IndexRoute, Route } from 'react-router'; +import WebApiApp from './components/WebApiApp'; + +export default [ + <IndexRoute key="index" component={WebApiApp}/>, + <Route key="splat" path="**" component={WebApiApp}/> +]; |