Skip to content

Commit

Permalink
Merge pull request #14243 from opf/implementation/50609-add-danger-zo…
Browse files Browse the repository at this point in the history
…ne-for-storage-delete

Added DangerZone for storage deletion in administration
  • Loading branch information
apfohl committed Nov 28, 2023
2 parents fe20c94 + a92ae5e commit 2369067
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 20 deletions.
Expand Up @@ -42,7 +42,7 @@ class Storages::Admin::StoragesController < ApplicationController
# and set the @<controller_name> variable to the object referenced in the URL.
before_action :require_admin
before_action :find_model_object,
only: %i[show show_oauth_application destroy edit edit_host update replace_oauth_application]
only: %i[show show_oauth_application destroy edit edit_host confirm_destroy update replace_oauth_application]

# menu_item is defined in the Redmine::MenuManager::MenuController
# module, included from ApplicationController.
Expand Down Expand Up @@ -153,6 +153,10 @@ def update # rubocop:disable Metrics/AbcSize
end
end

def confirm_destroy
@storage_to_destroy = @storage
end

def destroy
Storages::Storages::DeleteService
.new(user: User.current, model: @storage)
Expand Down
@@ -0,0 +1,64 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2023 the OpenProject GmbH
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
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 General Public License for more details.
You should have received a copy of the GNU 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.
See COPYRIGHT and LICENSE files for more details.
++#%>
<%= styled_form_tag(admin_settings_storage_path(@storage_to_destroy),
class: 'danger-zone',
method: :delete) do %>
<section class="form--section">
<h3 class="form--section-title">
<%= t('storages.page_titles.file_storages.delete') %>
</h3>
<p>
<%= t('storages.delete_warning.storage', file_storage: "<strong>#{h(@storage_to_destroy.name)}</strong>").html_safe %>
</p>
<ul class="mb-3">
<li> <%= t('storages.delete_warning.storage_delete_result_1') %>
<li> <%= t('storages.delete_warning.storage_delete_result_2') %>
<li> <%= t('storages.delete_warning.storage_delete_result_3') %>
</ul>
<p class="danger-zone--warning">
<span class="icon icon-error"></span>
<span><%= t('storages.delete_warning.irreversible_notice') %></span>
</p>
<p>
<%= t('storages.delete_warning.input_delete_confirmation', file_storage: "<em class=\"danger-zone--expected-value\">#{h(@storage_to_destroy.name)}</em>").html_safe %>
</p>
<div class="danger-zone--verification">
<%= text_field_tag :delete_confirmation %>
<%= styled_button_tag title: t(:button_delete), class: '-highlight', disabled: true do
concat content_tag :i, '', class: 'button--icon icon-delete'
concat content_tag :span, t(:button_delete), class: 'button--text'
end %>
<%= link_to admin_settings_storages_path,
title: t(:button_cancel),
class: 'button -with-icon icon-cancel' do %>
<%= t(:button_cancel) %>
<% end %>
</div>
</section>
<% end %>
19 changes: 9 additions & 10 deletions modules/storages/app/views/storages/admin/storages/edit.html.erb
Expand Up @@ -47,16 +47,15 @@ See COPYRIGHT and LICENSE files for more details.
<%=
primer_form_with(
model: @storage,
url: admin_settings_storage_path(@storage),
method: :delete
) do |_form|
render(
Primer::Beta::Button.new(
scheme: :danger,
size: :medium,
type: :submit,
aria: { label: I18n.t("storages.label_delete_storage") },
data: { confirm: I18n.t('storages.delete_warning.storage') },
url: confirm_destroy_admin_settings_storage_path(@storage),
method: :get
) do |_form|
render(
Primer::Beta::Button.new(
scheme: :danger,
size: :medium,
type: :submit,
aria: { label: I18n.t("storages.label_delete_storage") },
test_selector: 'storage-delete-button'
)
) do |button|
Expand Down
12 changes: 8 additions & 4 deletions modules/storages/config/locales/en.yml
Expand Up @@ -95,6 +95,7 @@ en:
page_titles:
file_storages:
subtitle: "Add an external file storage in order to upload, link and manage files in work packages."
delete: "Delete file storage"
managed_project_folders:
title: "Automatically managed project folders"
subtitle_short: "Let OpenProject create folders per project automatically."
Expand Down Expand Up @@ -166,11 +167,14 @@ en:
one_drive: "Allow OpenProject to access Azure data using OAuth to connect OneDrive/Sharepoint."
delete_warning:
storage: >
Are you sure you want to delete this storage? This will also delete the storage from all projects where it is used.
Further, it will also delete all links from work packages to files that are stored in that storage.
Are you sure you want to delete %{file_storage}? To confirm this action please introduce the
storage name in the field below, this will:
storage_delete_result_1: "Remove all storage setups for all projects using this storage."
storage_delete_result_2: "Remove all links from work packages of all projects to files and folders of that storage."
storage_delete_result_3: "In case this storage has an automatically managed project folder, the permissions to access the folder and its files will be removed."
project_storage: >
Are you sure you want to delete %{file_storage} from this project? To confirm this action please
introduce the storage name in the field below, this will:
Are you sure you want to delete %{file_storage} from this project? To confirm this action please
introduce the storage name in the field below, this will:
project_storage_delete_result_1: "Remove all links from work packages of this project to files and folders of that storage."
project_storage_delete_result_2: "In case this storage has an automatically managed project folder, this and its files will be deleted forever."
input_delete_confirmation: "Enter the file storage name %{file_storage} to confirm deletion."
Expand Down
1 change: 1 addition & 0 deletions modules/storages/config/routes.rb
Expand Up @@ -44,6 +44,7 @@
member do
get :show_oauth_application
get :edit_host
get :confirm_destroy
delete :replace_oauth_application
end
end
Expand Down
17 changes: 12 additions & 5 deletions modules/storages/spec/features/admin_storages_spec.rb
Expand Up @@ -291,19 +291,26 @@
end

describe 'File storage edit view' do
it 'renders a delete button' do
it 'renders a danger zone for deletion' do
storage = create(:nextcloud_storage, name: "Foo Nextcloud")
visit edit_admin_settings_storage_path(storage)

storage_delete_button = find_test_selector('storage-delete-button')
expect(storage_delete_button).to have_text('Delete')

accept_confirm do
storage_delete_button.click
end
storage_delete_button.click

expect(page).to have_text("DELETE FILE STORAGE")
expect(page).to have_current_path("#{confirm_destroy_admin_settings_storage_path(storage)}?utf8=%E2%9C%93")
storage_delete_button = page.find_button('Delete', disabled: true)

fill_in('delete_confirmation', with: 'Foo Nextcloud')
expect(storage_delete_button).not_to be_disabled

storage_delete_button.click

expect(page).to have_current_path(admin_settings_storages_path)
expect(page).not_to have_text("Foo Nextcloud")
expect(page).to have_current_path(admin_settings_storages_path)
end

context 'with Nextcloud Storage' do
Expand Down

0 comments on commit 2369067

Please sign in to comment.