From: Stas Vilchik Date: Tue, 26 Jul 2016 14:11:54 +0000 (+0200) Subject: SONAR-7918 Rewrite "Deletion" project page (#1116) X-Git-Tag: 6.1-RC1~510 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=fcba3fa1650b1afc794094e20764191ae8bca910;p=sonarqube.git SONAR-7918 Rewrite "Deletion" project page (#1116) --- diff --git a/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/project-deletion/project-deletion.html b/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/project-deletion/project-deletion.html index 16a057b896a..c77796182ba 100644 --- a/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/project-deletion/project-deletion.html +++ b/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/project-deletion/project-deletion.html @@ -55,43 +55,33 @@ open - /dashboard/index/sample + /project/deletion?id=sample - - click - css=#context-navigation .navbar-admin-link - - waitForElementPresent - link=Deletion + css=#delete-project - clickAndWait - link=Deletion + click + css=#delete-project - click - delete_resource - - - - waitForText - delete-project-form - *Delete Project* + waitForElementPresent + css=#delete-project-confirm + click - delete-project-submit + css=#delete-project-confirm - pause - 3000 - NOTE: necessary as the deletion is asynchronous + waitForElementNotPresent + css=#delete-project-confirm + open diff --git a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentNavigationAction.java b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentNavigationAction.java index f7aca74f29e..4d824d7806e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentNavigationAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentNavigationAction.java @@ -66,7 +66,6 @@ public class ComponentNavigationAction implements NavigationWsAction { 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; @@ -221,7 +220,6 @@ public class ComponentNavigationAction implements NavigationWsAction { 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())); } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/on_module.json b/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/on_module.json index 19646c8fa00..51e9d413003 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/on_module.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/on_module.json @@ -15,7 +15,6 @@ "showPermissions": false, "showHistory": false, "showUpdateKey": false, - "showDeletion": false, "extensions": [] }, "breadcrumbs": [ diff --git a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/quality_profile_admin.json b/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/quality_profile_admin.json index 3e6474a4420..594a12562e6 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/quality_profile_admin.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/quality_profile_admin.json @@ -14,8 +14,7 @@ "showLinks": false, "showPermissions": false, "showHistory": false, - "showUpdateKey": false, - "showDeletion": false + "showUpdateKey": false }, "breadcrumbs": [ { diff --git a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/with_admin_rights.json b/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/with_admin_rights.json index e60ff54767d..c213ac06368 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/with_admin_rights.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/with_admin_rights.json @@ -15,7 +15,6 @@ "showPermissions": false, "showHistory": false, "showUpdateKey": false, - "showDeletion": false, "extensions": [ { "name": "First Page", diff --git a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/with_all_properties.json b/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/with_all_properties.json index 2352e48e484..930df6729ef 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/with_all_properties.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentNavigationActionTest/with_all_properties.json @@ -15,7 +15,6 @@ "showPermissions": true, "showHistory": true, "showUpdateKey": true, - "showDeletion": true, "extensions": [] }, "breadcrumbs": [ diff --git a/server/sonar-web/src/main/js/api/components.js b/server/sonar-web/src/main/js/api/components.js index 3c7581dba9b..f0f49a636b4 100644 --- a/server/sonar-web/src/main/js/api/components.js +++ b/server/sonar-web/src/main/js/api/components.js @@ -39,6 +39,12 @@ export function deleteComponents (data) { 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); 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 new file mode 100644 index 00000000000..ee3493544d3 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/app.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 { 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 => + ; + + render(( + + + + ), el); +}); diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModal.js b/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModal.js new file mode 100644 index 00000000000..e5e6f712c02 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModal.js @@ -0,0 +1,47 @@ +/* + * 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 }; + } +}); + diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModalTemplate.hbs b/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModalTemplate.hbs new file mode 100644 index 00000000000..5da393e8d75 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModalTemplate.hbs @@ -0,0 +1,13 @@ +
+ + + +
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 new file mode 100644 index 00000000000..67563891ade --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js @@ -0,0 +1,37 @@ +/* + * 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 ( +
+
+
+
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js b/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js new file mode 100644 index 00000000000..81d52fb3ba2 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js @@ -0,0 +1,47 @@ +/* + * 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 ( + + + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/Header.js b/server/sonar-web/src/main/js/apps/project-admin/deletion/Header.js new file mode 100644 index 00000000000..39bd303693f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/deletion/Header.js @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { translate } from '../../../helpers/l10n'; + +export default class Header extends React.Component { + render () { + return ( +
+

+ {translate('deletion.page')} +

+
+ {translate('project_deletion.page.description')} +
+
+ ); + } +} diff --git a/server/sonar-web/src/main/js/main/nav/app.js b/server/sonar-web/src/main/js/main/nav/app.js index 9c0526f57a0..902e6ea07ef 100644 --- a/server/sonar-web/src/main/js/main/nav/app.js +++ b/server/sonar-web/src/main/js/main/nav/app.js @@ -67,8 +67,12 @@ export default class App { 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(, el); + ReactDOM.render(, el); } return component; }); diff --git a/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js b/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js index 4aaf6ff239e..ad6ad6c6e76 100644 --- a/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js +++ b/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js @@ -42,12 +42,11 @@ export default React.createClass({ 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'; }, @@ -100,7 +99,6 @@ export default React.createClass({ renderAdministration() { const shouldShowAdministration = this.props.conf.showBackgroundTasks || - this.props.conf.showDeletion || this.props.conf.showHistory || this.props.conf.showLinks || this.props.conf.showManualMeasures || @@ -132,8 +130,8 @@ export default React.createClass({ {this.renderHistoryLink()} {this.renderBackgroundTasksLink()} {this.renderUpdateKeyLink()} - {this.renderDeletionLink()} {this.renderExtensions()} + {this.renderDeletionLink()} ); @@ -212,9 +210,12 @@ export default React.createClass({ }, 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'); }, diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb index a85983ebede..ab0d5400963 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb @@ -20,7 +20,6 @@ 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 @@ -29,56 +28,8 @@ class ProjectController < ApplicationController 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= @@ -227,18 +178,6 @@ class ProjectController < ApplicationController @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]) diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/_delete_form.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/_delete_form.html.erb deleted file mode 100644 index 79c73cd6f82..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/_delete_form.html.erb +++ /dev/null @@ -1,34 +0,0 @@ -<% resource_qualifier = message('qualifier.' + @project.qualifier) %> -
-
- - - -
-
- diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/deletion.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/deletion.html.erb index da66ac28b95..e9dd9ae3410 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/deletion.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/deletion.html.erb @@ -1,18 +1,3 @@ -
- <% - if !@snapshot || @project.root? - resource_qualifier = message('qualifier.' + @project.qualifier) - delete_resource_message = message('project_deletion.page', :params => resource_qualifier) - %> - - -
-
<%= message('project_deletion.operation_cannot_be_undone') -%>
- <%= delete_resource_message -%> -
- <% end %> -
+<% content_for :extra_script do %> + +<% end %> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/pending_deletion.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/pending_deletion.html.erb deleted file mode 100644 index 7d96f93b194..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/pending_deletion.html.erb +++ /dev/null @@ -1,2 +0,0 @@ -<%= 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 diff --git a/server/sonar-web/webpack.config.js b/server/sonar-web/webpack.config.js index d64f64ab975..cea911812e7 100644 --- a/server/sonar-web/webpack.config.js +++ b/server/sonar-web/webpack.config.js @@ -45,6 +45,7 @@ module.exports = { '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', diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index c2ed6ed763c..94cab9055d4 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -508,8 +508,7 @@ bulk_deletion.page.description=Use this page to delete multiple projects at once 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. @@ -1640,8 +1639,7 @@ project_quality_gate.default_qgate=Default # #------------------------------------------------------------------------------ -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}"? #------------------------------------------------------------------------------