Browse Source

SONAR-8659 Create organization's projects management page

tags/6.3-RC1
Stas Vilchik 7 years ago
parent
commit
ab71ca77ca

+ 7
- 5
server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.js View File

@@ -155,11 +155,13 @@ class SettingsNav extends React.Component {
{translate('sidebar.projects')} <i className="icon-dropdown"/>
</a>
<ul className="dropdown-menu">
<li>
<IndexLink to="/projects_admin" activeClassName="active">
Management
</IndexLink>
</li>
{!this.props.customOrganizations && (
<li>
<IndexLink to="/projects_admin" activeClassName="active">
Management
</IndexLink>
</li>
)}
<li>
<IndexLink to="/background_tasks" activeClassName="active">
{translate('background_tasks.page')}

+ 43
- 0
server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjectsManagement.js View File

@@ -0,0 +1,43 @@
/*
* 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.
*/
// @flow
import React from 'react';
import { connect } from 'react-redux';
import AppContainer from '../../projects-admin/AppContainer';
import { getOrganizationByKey } from '../../../store/rootReducer';
import type { Organization } from '../../../store/organizations/duck';

class OrganizationProjectsManagement extends React.Component {
props: {
organization: Organization
};

render () {
return (
<AppContainer organization={this.props.organization}/>
);
}
}

const mapStateToProps = (state, ownProps) => ({
organization: getOrganizationByKey(state, ownProps.params.organizationKey)
});

export default connect(mapStateToProps)(OrganizationProjectsManagement);

+ 10
- 2
server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js View File

@@ -28,7 +28,8 @@ const ADMIN_PATHS = [
'groups',
'delete',
'permissions',
'permission_templates'
'permission_templates',
'projects_management'
];

export default class OrganizationNavigation extends React.Component {
@@ -72,6 +73,11 @@ export default class OrganizationNavigation extends React.Component {
{translate('permission_templates')}
</Link>
</li>
<li>
<Link to={`/organizations/${organization.key}/projects_management`} activeClassName="active">
{translate('projects_management')}
</Link>
</li>
<li>
<Link to={`/organizations/${organization.key}/edit`} activeClassName="active">
{translate('edit')}
@@ -90,7 +96,9 @@ export default class OrganizationNavigation extends React.Component {
render () {
const { organization, location } = this.props;

const isHomeActive = location.pathname.startsWith(`organizations/${organization.key}/projects`);
const isHomeActive =
location.pathname === `organizations/${organization.key}/projects` ||
location.pathname === `organizations/${organization.key}/projects/favorite`;

return (
<nav className="navbar navbar-context page-container" id="context-navigation">

+ 9
- 0
server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap View File

@@ -75,6 +75,15 @@ exports[`test admin 1`] = `
permission_templates
</Link>
</li>
<li>
<Link
activeClassName="active"
onlyActiveOnIndex={false}
style={Object {}}
to="/organizations/foo/projects_management">
projects_management
</Link>
</li>
<li>
<Link
activeClassName="active"

+ 2
- 0
server/sonar-web/src/main/js/apps/organizations/routes.js View File

@@ -27,6 +27,7 @@ import OrganizationEdit from './components/OrganizationEdit';
import OrganizationGroups from './components/OrganizationGroups';
import OrganizationPermissions from './components/OrganizationPermissions';
import OrganizationPermissionTemplates from './components/OrganizationPermissionTemplates';
import OrganizationProjectsManagement from './components/OrganizationProjectsManagement';
import OrganizationDelete from './components/OrganizationDelete';

export default (
@@ -40,6 +41,7 @@ export default (
<Route path="groups" component={OrganizationGroups}/>
<Route path="permissions" component={OrganizationPermissions}/>
<Route path="permission_templates" component={OrganizationPermissionTemplates}/>
<Route path="projects_management" component={OrganizationProjectsManagement}/>
</Route>
</Route>
);

+ 10
- 3
server/sonar-web/src/main/js/apps/permissions/project/views/ApplyTemplateView.js View File

@@ -33,7 +33,10 @@ export default ModalForm.extend({
},

loadPermissionTemplates () {
return getPermissionTemplates(this.options.project.organization).then(r => {
const request = this.options.organization ?
getPermissionTemplates(this.options.organization.key) :
getPermissionTemplates();
return request.then(r => {
this.permissionTemplates = r.permissionTemplates;
this.render();
});
@@ -52,10 +55,14 @@ export default ModalForm.extend({
const permissionTemplate = this.$('#project-permissions-template').val();
this.disableForm();

applyTemplateToProject({
const data = {
projectKey: this.options.project.key,
templateId: permissionTemplate
}).then(() => {
};
if (this.options.organization) {
data.organization = this.options.organization.key;
}
applyTemplateToProject(data).then(() => {
this.trigger('done');
this.done = true;
this.render();

+ 5
- 2
server/sonar-web/src/main/js/apps/projects-admin/AppContainer.js View File

@@ -25,12 +25,15 @@ import { getRootQualifiers } from '../../store/appState/duck';

class AppContainer extends React.Component {
render () {
const hasProvisionPermission = this.props.user.permissions.global.indexOf('provisioning') !== -1;
const hasProvisionPermission = this.props.organization ?
this.props.organization.canProvisionProjects :
this.props.user.permissions.global.indexOf('provisioning') !== -1;

return (
<Main
hasProvisionPermission={hasProvisionPermission}
topLevelQualifiers={this.props.rootQualifiers}/>
topLevelQualifiers={this.props.rootQualifiers}
organization={this.props.organization}/>
);
}
}

+ 4
- 3
server/sonar-web/src/main/js/apps/projects-admin/create-view.js View File

@@ -45,6 +45,9 @@ export default ModalForm.extend({
branch: this.$('#create-project-branch').val(),
key: this.$('#create-project-key').val()
};
if (this.options.organization) {
data.organization = this.options.organization.key;
}
this.disableForm();
return createProject(data)
.then(project => {
@@ -57,9 +60,7 @@ export default ModalForm.extend({
})
.catch(error => {
this.enableForm();
if (error.response.status === 400) {
error.response.json().then(obj => this.showErrors([{ msg: obj.err_msg }]));
}
error.response.json().then(r => this.showErrors(r.errors, r.warnings));
});
},


+ 4
- 2
server/sonar-web/src/main/js/apps/projects-admin/header.js View File

@@ -28,7 +28,8 @@ export default class Header extends React.Component {

createProject () {
new CreateView({
refresh: this.props.refresh
refresh: this.props.refresh,
organization: this.props.organization
}).render();
}

@@ -37,7 +38,8 @@ export default class Header extends React.Component {
total: this.props.total,
selection: this.props.selection,
query: this.props.query,
qualifier: this.props.qualifier
qualifier: this.props.qualifier,
organization: this.props.organization
}).render();
}


+ 14
- 4
server/sonar-web/src/main/js/apps/projects-admin/main.js View File

@@ -30,7 +30,8 @@ import ListFooter from '../../components/controls/ListFooter';

export default React.createClass({
propTypes: {
hasProvisionPermission: React.PropTypes.bool.isRequired
hasProvisionPermission: React.PropTypes.bool.isRequired,
organization: React.PropTypes.object
},

getInitialState () {
@@ -62,6 +63,9 @@ export default React.createClass({
if (this.state.query) {
filters.q = this.state.query;
}
if (this.props.organization) {
filters.organization = this.props.organization.key;
}
return filters;
},

@@ -182,7 +186,11 @@ export default React.createClass({
deleteProjects () {
this.setState({ ready: false });
const ids = this.state.selection.join(',');
deleteComponents({ ids }).then(() => {
const data = { ids };
if (this.props.organization) {
Object.assign(data, { organization: this.props.organization.key });
}
deleteComponents(data).then(() => {
this.setState({ page: 1, selection: [] }, this.requestProjects);
});
},
@@ -196,7 +204,8 @@ export default React.createClass({
total={this.state.total}
query={this.state.query}
qualifier={this.state.qualifiers}
refresh={this.requestProjects}/>
refresh={this.requestProjects}
organization={this.props.organization}/>

<Search {...this.props} {...this.state}
onSearch={this.onSearch}
@@ -212,7 +221,8 @@ export default React.createClass({
refresh={this.requestProjects}
selection={this.state.selection}
onProjectSelected={this.onProjectSelected}
onProjectDeselected={this.onProjectDeselected}/>
onProjectDeselected={this.onProjectDeselected}
organization={this.props.organization}/>

<ListFooter
ready={this.state.ready}

+ 6
- 2
server/sonar-web/src/main/js/apps/projects-admin/projects.js View File

@@ -29,7 +29,8 @@ import { translate } from '../../helpers/l10n';
export default class Projects extends React.Component {
static propTypes = {
projects: React.PropTypes.array.isRequired,
selection: React.PropTypes.array.isRequired
selection: React.PropTypes.array.isRequired,
organization: React.PropTypes.object
};

componentWillMount () {
@@ -47,7 +48,10 @@ export default class Projects extends React.Component {
onApplyTemplateClick (project, e) {
e.preventDefault();
e.target.blur();
new ApplyTemplateView({ project }).render();
new ApplyTemplateView({
project,
organization: this.props.organization
}).render();
}

isProjectSelected (project) {

+ 12
- 2
server/sonar-web/src/main/js/apps/projects-admin/views/BulkApplyTemplateView.js View File

@@ -34,7 +34,10 @@ export default ModalForm.extend({
},

loadPermissionTemplates () {
return getPermissionTemplates().then(r => {
const request = this.options.organization ?
getPermissionTemplates(this.options.organization.key) :
getPermissionTemplates();
return request.then(r => {
this.permissionTemplates = r.permissionTemplates;
this.render();
});
@@ -59,6 +62,10 @@ export default ModalForm.extend({
data.qualifier = this.options.qualifier;
}

if (this.options.organization) {
data.organization = this.options.organization.key;
}

return bulkApplyTemplate(data);
},

@@ -68,6 +75,9 @@ export default ModalForm.extend({

selection.forEach(projectId => {
const data = { templateId: permissionTemplate, projectId };
if (this.options.organization) {
data.organization = this.options.organization.key;
}
lastRequest = lastRequest.then(() => applyTemplateToProject(data));
});

@@ -88,7 +98,7 @@ export default ModalForm.extend({
this.trigger('done');
this.done = true;
this.render();
}).catch(function (e) {
}).catch(e => {
e.response.json().then(r => {
this.showErrors(r.errors, r.warnings);
this.enableForm();

+ 1
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -126,6 +126,7 @@ permalinks=Permalinks
plugin=Plugin
project=Project
projects=Projects
projects_management=Projects Management
quality_profile=Quality Profile
raw=Raw
recent_history=Recent History

Loading…
Cancel
Save