@@ -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')} |
@@ -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); |
@@ -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"> |
@@ -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" |
@@ -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> | |||
); |
@@ -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(); |
@@ -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}/> | |||
); | |||
} | |||
} |
@@ -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)); | |||
}); | |||
}, | |||
@@ -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(); | |||
} | |||
@@ -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} |
@@ -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) { |
@@ -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(); |
@@ -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 |