</tr>
<tr>
<td>open</td>
- <td>/dashboard/index/sample</td>
+ <td>/project/deletion?id=sample</td>
<td></td>
</tr>
-<tr>
- <td>click</td>
- <td>css=#context-navigation .navbar-admin-link</td>
- <td></td>
-</tr>
<tr>
<td>waitForElementPresent</td>
- <td>link=Deletion</td>
+ <td>css=#delete-project</td>
<td></td>
</tr>
<tr>
- <td>clickAndWait</td>
- <td>link=Deletion</td>
+ <td>click</td>
+ <td>css=#delete-project</td>
<td></td>
</tr>
<tr>
- <td>click</td>
- <td>delete_resource</td>
- <td></td>
-</tr>
-<tr>
- <td>waitForText</td>
- <td>delete-project-form</td>
- <td>*Delete Project*</td>
+ <td>waitForElementPresent</td>
+ <td>css=#delete-project-confirm</td>
+ <td></td>
</tr>
<tr>
<td>click</td>
- <td>delete-project-submit</td>
+ <td>css=#delete-project-confirm</td>
<td></td>
</tr>
<tr>
- <td>pause</td>
- <td>3000</td>
- <td>NOTE: necessary as the deletion is asynchronous</td>
+ <td>waitForElementNotPresent</td>
+ <td>css=#delete-project-confirm</td>
+ <td></td>
</tr>
<tr>
<td>open</td>
private static final String PROPERTY_HAS_ROLE_POLICY = "hasRolePolicy";
private static final String PROPERTY_MODIFIABLE_HISTORY = "modifiable_history";
private static final String PROPERTY_UPDATABLE_KEY = "updatable_key";
- private static final String PROPERTY_DELETABLE = "deletable";
private final DbClient dbClient;
private final Views views;
json.prop("showPermissions", isAdmin && componentTypeHasProperty(component, PROPERTY_HAS_ROLE_POLICY));
json.prop("showHistory", isAdmin && componentTypeHasProperty(component, PROPERTY_MODIFIABLE_HISTORY));
json.prop("showUpdateKey", isAdmin && componentTypeHasProperty(component, PROPERTY_UPDATABLE_KEY));
- json.prop("showDeletion", isAdmin && componentTypeHasProperty(component, PROPERTY_DELETABLE));
json.prop("showBackgroundTasks", ActivityAction.isAllowedOnComponentUuid(userSession, component.uuid()));
}
"showPermissions": false,
"showHistory": false,
"showUpdateKey": false,
- "showDeletion": false,
"extensions": []
},
"breadcrumbs": [
"showLinks": false,
"showPermissions": false,
"showHistory": false,
- "showUpdateKey": false,
- "showDeletion": false
+ "showUpdateKey": false
},
"breadcrumbs": [
{
"showPermissions": false,
"showHistory": false,
"showUpdateKey": false,
- "showDeletion": false,
"extensions": [
{
"name": "First Page",
"showPermissions": true,
"showHistory": true,
"showUpdateKey": true,
- "showDeletion": true,
"extensions": []
},
"breadcrumbs": [
return post(url, data);
}
+export function deleteProject (key) {
+ const url = '/api/projects/delete';
+ const data = { key };
+ return post(url, data);
+}
+
export function createProject (data) {
const url = '/api/projects/create';
return postJSON(url, data);
--- /dev/null
+/*
+ * 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, useRouterHistory } from 'react-router';
+import { createHistory } from 'history';
+import Deletion from './deletion/Deletion';
+
+window.sonarqube.appStarted.then(options => {
+ const el = document.querySelector(options.el);
+
+ const history = useRouterHistory(createHistory)({
+ basename: window.baseUrl + '/project'
+ });
+
+ const withComponent = ComposedComponent => props =>
+ <ComposedComponent {...props} component={options.component}/>;
+
+ render((
+ <Router history={history}>
+ <Route path="/deletion" component={withComponent(Deletion)}/>
+ </Router>
+ ), el);
+});
--- /dev/null
+/*
+ * 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 ModalForm from '../../../components/common/modal-form';
+import Template from './ConfirmationModalTemplate.hbs';
+import { deleteProject } from '../../../api/components';
+
+export default ModalForm.extend({
+ template: Template,
+
+ onFormSubmit () {
+ ModalForm.prototype.onFormSubmit.apply(this, arguments);
+ this.disableForm();
+
+ deleteProject(this.options.project.key)
+ .then(() => {
+ this.trigger('done');
+ })
+ .catch(function (e) {
+ e.response.json().then(r => {
+ this.showErrors(r.errors, r.warnings);
+ this.enableForm();
+ });
+ });
+ },
+
+ serializeData () {
+ return { project: this.options.project };
+ }
+});
+
--- /dev/null
+<form id="deactivate-user-form" autocomplete="off">
+ <div class="modal-head">
+ <h2>{{t 'qualifiers.delete.TRK'}}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ {{tp 'project_deletion.delete_resource_confirmation' project.name}}
+ </div>
+ <div class="modal-foot">
+ <button id="delete-project-confirm" class="button-red">{{t 'delete'}}</button>
+ <a href="#" class="js-modal-close">{{t 'cancel'}}</a>
+ </div>
+</form>
--- /dev/null
+/*
+ * 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 Header from './Header';
+import Form from './Form';
+
+export default class Deletion extends React.Component {
+ static propTypes = {
+ component: React.PropTypes.object.isRequired
+ };
+
+ render () {
+ return (
+ <div className="page page-limited">
+ <Header/>
+ <Form component={this.props.component}/>
+ </div>
+ );
+ }
+}
--- /dev/null
+/*
+ * 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 ConfirmationModal from './ConfirmationModal';
+import { translate } from '../../../helpers/l10n';
+
+export default class Form extends React.Component {
+ static propTypes = {
+ component: React.PropTypes.object.isRequired
+ };
+
+ handleDelete (e) {
+ e.preventDefault();
+ new ConfirmationModal({ project: this.props.component })
+ .on('done', () => {
+ window.location = window.baseUrl + '/';
+ })
+ .render();
+ }
+
+ render () {
+ return (
+ <form onSubmit={this.handleDelete.bind(this)}>
+ <button id="delete-project" className="button-red">
+ {translate('delete')}
+ </button>
+ </form>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React from 'react';
+import { translate } from '../../../helpers/l10n';
+
+export default class Header extends React.Component {
+ render () {
+ return (
+ <header className="page-header">
+ <h1 className="page-title">
+ {translate('deletion.page')}
+ </h1>
+ <div className="page-description">
+ {translate('project_deletion.page.description')}
+ </div>
+ </header>
+ );
+ }
+}
static renderComponentNav (options) {
return getComponentNavigation(options.componentKey).then(component => {
const el = document.getElementById('context-navigation');
+ const nextComponent = {
+ ...component,
+ qualifier: _.last(component.breadcrumbs).qualifier
+ };
if (el) {
- ReactDOM.render(<ComponentNav component={component} conf={component.configuration || {}}/>, el);
+ ReactDOM.render(<ComponentNav component={nextComponent} conf={component.configuration || {}}/>, el);
}
return component;
});
mixins: [LinksMixin],
isDeveloper() {
- const qualifier = _.last(this.props.component.breadcrumbs).qualifier;
- return qualifier === 'DEV';
+ return this.props.component.qualifier === 'DEV';
},
isView() {
- const qualifier = _.last(this.props.component.breadcrumbs).qualifier;
+ const { qualifier } = this.props.component;
return qualifier === 'VW' || qualifier === 'SVW';
},
renderAdministration() {
const shouldShowAdministration =
this.props.conf.showBackgroundTasks ||
- this.props.conf.showDeletion ||
this.props.conf.showHistory ||
this.props.conf.showLinks ||
this.props.conf.showManualMeasures ||
{this.renderHistoryLink()}
{this.renderBackgroundTasksLink()}
{this.renderUpdateKeyLink()}
- {this.renderDeletionLink()}
{this.renderExtensions()}
+ {this.renderDeletionLink()}
</ul>
</li>
);
},
renderDeletionLink() {
- if (!this.props.conf.showDeletion) {
+ const { qualifier } = this.props.component;
+
+ if (qualifier !== 'TRK' && qualifier !== 'VW') {
return null;
}
+
const url = `/project/deletion?id=${encodeURIComponent(this.props.component.key)}`;
return this.renderLink(url, translate('deletion.page'), '/project/deletion');
},
class ProjectController < ApplicationController
verify :method => :post, :only => [:set_links, :set_exclusions, :delete_exclusions, :update_key, :perform_key_bulk_update],
:redirect_to => {:action => :index}
- verify :method => :delete, :only => [:delete], :redirect_to => {:action => :index}
SECTION=Navigation::SECTION_RESOURCE
redirect_to :overwrite_params => {:controller => :dashboard, :action => 'index'}
end
- def delete_form
- @project = get_current_project(params[:id])
- render :partial => 'delete_form'
- end
-
- def delete
- @project = get_current_project(params[:id])
-
- # Ask the resource deletion manager to start the migration
- # => this is an asynchronous AJAX call
- ResourceDeletionManager.instance.delete_resources([@project.id])
-
- # and return some text that will actually never be displayed
- render :text => ResourceDeletionManager.instance.message
- end
-
def deletion
@project = get_current_project(params[:id])
-
- if java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'deletable')
- deletion_manager = ResourceDeletionManager.instance
- if deletion_manager.currently_deleting_resources? ||
- (!deletion_manager.currently_deleting_resources? && deletion_manager.deletion_failures_occured?)
- # a deletion is happening or it has just finished with errors => display the message from the Resource Deletion Manager
- render :template => 'project/pending_deletion'
- else
- @snapshot=@project.last_snapshot
- end
- else
- redirect_to :action => 'index', :id => params[:id]
- end
- end
-
- def pending_deletion
- deletion_manager = ResourceDeletionManager.instance
-
- if deletion_manager.currently_deleting_resources? ||
- (!deletion_manager.currently_deleting_resources? && deletion_manager.deletion_failures_occured?)
- # display the same page again and again
- # => implicit render "pending_deletion.html.erb"
- else
- redirect_to_default
- end
- end
-
- def dismiss_deletion_message
- # It is important to reinit the ResourceDeletionManager so that the deletion screens can be available again
- ResourceDeletionManager.instance.reinit
-
- redirect_to :action => 'deletion', :id => params[:id]
end
# GET /project/profile?id=<project id>
@project = get_current_project(params[:id])
end
- def delete_snapshot_history
- @project = get_current_project(params[:id])
-
- sid = params[:snapshot_id]
- if sid
- Snapshot.update_all("status='U'", ["id=?", sid.to_i])
- flash[:notice] = message('project_history.snapshot_deleted')
- end
-
- redirect_to :action => 'history', :id => @project.id
- end
-
def links
@project = get_current_project(params[:id])
+++ /dev/null
-<% resource_qualifier = message('qualifier.' + @project.qualifier) %>
-<form id="delete-project-form" method="post" action="<%= ApplicationController.root_context -%>/project/delete">
- <fieldset>
- <div class="modal-head">
- <h2><%= message('project_deletion.page', :params => resource_qualifier) -%></h2>
- </div>
- <div class="modal-body">
- <%= message('project_deletion.delete_resource_confirmation', :params => resource_qualifier) %>
- </div>
- <div class="modal-foot">
- <span id="delete-project-loading-image" class="loading-image hidden"><%= image_tag 'loading.gif' %></span>
- <input type="submit" value="<%= message('delete') -%>" id="delete-project-submit" onclick="return displayLoadingImage()"/>
- <a href="#" onclick="return closeModalWindow()" id="delete-project-cancel"><%= message('cancel') -%></a>
- </div>
- </fieldset>
-</form>
-<script>
- $j("#delete-project-form").modalForm({
- success: function () {
- $j.ajax({
- url: "<%= ApplicationController.root_context-%>/project/delete/<%= h(@project.id) -%>",
- success: function (request) {
- window.location = '<%= url_for(:action => 'pending_deletion',:id => @project.id)-%>';
- },
- data: $j(this).serialize(),
- type: 'delete'
- });
- }
- });
-
- function displayLoadingImage() {
- $j('#delete-project-loading-image').removeClass("hidden");
- }
-</script>
-<div class="page">
- <%
- if !@snapshot || @project.root?
- resource_qualifier = message('qualifier.' + @project.qualifier)
- delete_resource_message = message('project_deletion.page', :params => resource_qualifier)
- %>
- <header class="page-header">
- <h1 class="page-title"><%= delete_resource_message -%></h1>
- <p class="page-description"><%= message('project_deletion.page.description') -%></p>
- </header>
-
- <div class="yui-g widget" id="widget_delete_project">
- <div class="alert alert-warning spacer-bottom"><%= message('project_deletion.operation_cannot_be_undone') -%></div>
- <a id="delete_resource" class="open-modal button button-red"
- href="<%= ApplicationController.root_context -%>/project/delete_form/<%= h(@project.id) -%>"><%= delete_resource_message -%></a>
- </div>
- <% end %>
-</div>
+<% content_for :extra_script do %>
+ <script src="<%= ApplicationController.root_context -%>/js/bundles/project-admin.js?v=<%= sonar_version -%>"></script>
+<% end %>
+++ /dev/null
-<%= render :partial => 'bulk_deletion/pending_deletions_screen',
- :locals => {:url_after_dismiss => url_for(:action => 'dismiss_deletion_message', :id => params[:id])} -%>
\ No newline at end of file
'metrics': './src/main/js/apps/metrics/app.js',
'overview': './src/main/js/apps/overview/app.js',
'permission-templates': './src/main/js/apps/permission-templates/app.js',
+ 'project-admin': './src/main/js/apps/project-admin/app.js',
'project-permissions': './src/main/js/apps/permissions/project/app.js',
'projects': './src/main/js/apps/projects/app.js',
'quality-gates': './src/main/js/apps/quality-gates/app.js',
update_key.page=Update Key
update_key.page.description=Edit the keys of a project and/or its modules. Key changes must be made here BEFORE analyzing the project with the new keys, otherwise the analysis will simply create another project with the new key, rather than updating the existing project.
deletion.page=Deletion
-project_deletion.page=Delete {0}
-project_deletion.page.description=Delete this project from the SonarQube database.
+project_deletion.page.description=Delete this project from SonarQube. The operation cannot be undone.
provisioning.page=Provisioning
provisioning.page.description=Use this page to initialize projects if you would like to configure them before the first analysis. Once a project is provisioned, you have access to perform all project configurations on it.
#
#------------------------------------------------------------------------------
-project_deletion.operation_cannot_be_undone=This operation can not be undone.
-project_deletion.delete_resource_confirmation=Are you sure you want to delete this {0}?
+project_deletion.delete_resource_confirmation=Are you sure you want to delete "{0}"?
#------------------------------------------------------------------------------