Commit 80637ccb authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Allow immediate deletion of groups

When a group is already scheduled for deletion, we allow users to
immediately delete the group by going to the settings again and
confirming the deletion.

Changelog: added
EE: true
parent d0fe524e
......@@ -143,7 +143,7 @@ module GroupsHelper
end
def remove_group_message(group)
_("You are going to remove %{group_name}, this will also delete all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?") %
_("You are going to remove %{group_name}. This will also delete all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?") %
{ group_name: group.name }
end
......
......@@ -28,3 +28,4 @@
= render 'groups/settings/transfer', group: @group
= render 'groups/settings/remove', group: @group
= render_if_exists 'groups/settings/restore', group: @group
= render_if_exists 'groups/settings/immediately_remove', group: @group
......@@ -430,6 +430,28 @@ Specifically:
- In [GitLab 13.6 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/39504), if the user who sets up the deletion is removed from the group before the
deletion happens, the job is cancelled, and the group is no longer scheduled for deletion.
## Remove a group immediately **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336985) in GitLab 14.2.
If you don't want to wait, you can remove a group immediately.
Prerequisites:
- You must have at least the Owner role for a group.
- You have [marked the group for deletion](#remove-a-group).
To immediately remove a group marked for deletion:
1. On the top bar, select **Menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. In the "Permanently remove group" section, select **Remove group**.
1. Confirm the action when asked to.
Your group, its subgroups, projects, and all related resources, including issues and merge requests,
are deleted.
## Restore a group **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33257) in GitLab 12.8.
......
......@@ -32,6 +32,7 @@ module EE
override :destroy
def destroy
return super unless group.adjourned_deletion?
return super if group.marked_for_deletion? && ::Gitlab::Utils.to_boolean(params[:permanently_remove])
result = ::Groups::MarkForDeletionService.new(group, current_user).execute
......
......@@ -51,6 +51,7 @@ module EE
override :remove_group_message
def remove_group_message(group)
return super unless group.licensed_feature_available?(:adjourned_deletion_for_projects_and_groups)
return super if group.marked_for_deletion?
date = permanent_deletion_date(Time.now.utc)
......@@ -58,6 +59,18 @@ module EE
{ date: date, deletion_adjourned_period: deletion_adjourned_period }
end
def immediately_remove_group_message(group)
message = _('This action will %{strongOpen}permanently remove%{strongClose} %{codeOpen}%{group}%{codeClose} %{strongOpen}immediately%{strongClose}.')
html_escape(message) % {
group: group.path,
strongOpen: '<strong>'.html_safe,
strongClose: '</strong>'.html_safe,
codeOpen: '<code>'.html_safe,
codeClose: '</code>'.html_safe
}
end
def permanent_deletion_date(date)
(date + deletion_adjourned_period.days).strftime('%F')
end
......
- if group.marked_for_deletion?
.sub-section
%h4.gl-text-red-500= _('Permanently remove group')
= form_tag(group, method: :delete) do
%p
%strong= _('Removing this group also removes all child projects, including archived projects, and their resources.')
%p= immediately_remove_group_message(group)
%p
%strong= _('Are you ABSOLUTELY SURE you wish to remove this group?')
= hidden_field_tag(:permanently_remove, true)
= render 'groups/settings/remove_button', group: group
......@@ -221,6 +221,32 @@ RSpec.describe GroupsController do
expect(flash[:alert]).to include 'error'
end
end
context 'when group is already marked for deletion' do
before do
create(:group_deletion_schedule, group: group, marked_for_deletion_on: Date.current)
end
context 'when permanently_remove param is set' do
it 'deletes the group immediately' do
expect(GroupDestroyWorker).to receive(:perform_async)
delete :destroy, params: { id: group.to_param, permanently_remove: true }
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to include "Group '#{group.name}' was scheduled for deletion."
end
end
context 'when permanently_remove param is not set' do
it 'does nothing' do
subject
expect(response).to redirect_to(edit_group_path(group))
expect(flash[:alert]).to include "Group has been already marked for deletion"
end
end
end
end
context 'delayed deletion feature is not available' do
......
......@@ -158,16 +158,36 @@ RSpec.describe 'Edit group settings' do
stub_licensed_features(adjourned_deletion_for_projects_and_groups: true)
end
let_it_be(:subgroup) { create(:group, parent: group) }
it_behaves_like 'a cascading setting' do
let_it_be(:subgroup) { create(:group, parent: group) }
let(:form_group_selector) { '[data-testid="delayed-project-removal-form-group"]' }
let(:setting_field_selector) { '[data-testid="delayed-project-removal-checkbox"]' }
let(:setting_path) { edit_group_path(group, anchor: 'js-permissions-settings') }
let(:group_path) { edit_group_path(group) }
let(:subgroup_path) { edit_group_path(subgroup) }
let(:click_save_button) { save_permissions_group }
end
describe 'immediately deleting a project marked for deletion', :js do
before do
create(:group_deletion_schedule, group: group, marked_for_deletion_on: 2.days.from_now)
visit edit_group_path(group)
end
it 'deletes the project immediately', :sidekiq_inline do
expect { remove_with_confirm('Remove group', group.path) }.to change { Group.count }.by(-1)
let(:form_group_selector) { '[data-testid="delayed-project-removal-form-group"]' }
let(:setting_field_selector) { '[data-testid="delayed-project-removal-checkbox"]' }
let(:setting_path) { edit_group_path(group, anchor: 'js-permissions-settings') }
let(:group_path) { edit_group_path(group) }
let(:subgroup_path) { edit_group_path(subgroup) }
let(:click_save_button) { save_permissions_group }
expect(page).to have_content "scheduled for deletion"
end
it_behaves_like 'a cascading setting'
def remove_with_confirm(button_text, confirm_with, confirm_button_text = 'Confirm')
click_button button_text
fill_in 'confirm_name_input', with: confirm_with
click_button confirm_button_text
end
end
end
context 'when custom_project_templates feature' do
......
......@@ -176,6 +176,17 @@ RSpec.describe GroupsHelper do
it 'returns the message related to delayed deletion' do
expect(subject).to include("The contents of this group, its subgroups and projects will be permanently removed after")
end
context 'group is already marked for deletion' do
before do
create(:group_deletion_schedule, group: group, marked_for_deletion_on: Date.current)
end
it 'returns the message related to permanent deletion' do
expect(subject).to include("You are going to remove #{group.name}")
expect(subject).to include("Removed groups CANNOT be restored!")
end
end
end
context 'delayed deletion feature is not available' do
......@@ -190,6 +201,14 @@ RSpec.describe GroupsHelper do
end
end
describe '#immediately_remove_group_message' do
subject { helper.immediately_remove_group_message(group) }
it 'returns the message related to immediate deletion' do
expect(subject).to match(/permanently remove.*#{group.path}.*immediately/)
end
end
describe '#show_discover_group_security?' do
using RSpec::Parameterized::TableSyntax
......
......@@ -4299,6 +4299,9 @@ msgstr ""
msgid "Are you ABSOLUTELY SURE you wish to delete this project?"
msgstr ""
msgid "Are you ABSOLUTELY SURE you wish to remove this group?"
msgstr ""
msgid "Are you sure that you want to archive this project?"
msgstr ""
......@@ -24042,6 +24045,9 @@ msgstr ""
msgid "Permanently delete project"
msgstr ""
msgid "Permanently remove group"
msgstr ""
msgid "Permissions"
msgstr ""
......@@ -33646,6 +33652,9 @@ msgstr ""
msgid "This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}on %{date}%{strongClose}, including its repositories and all related resources, including issues and merge requests."
msgstr ""
msgid "This action will %{strongOpen}permanently remove%{strongClose} %{codeOpen}%{group}%{codeClose} %{strongOpen}immediately%{strongClose}."
msgstr ""
msgid "This also resolves all related threads"
msgstr ""
......@@ -37576,7 +37585,7 @@ msgstr ""
msgid "You are going to delete %{project_full_name}. Deleted projects CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr ""
msgid "You are going to remove %{group_name}, this will also delete all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgid "You are going to remove %{group_name}. This will also delete all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr ""
msgid "You are going to remove the fork relationship from %{project_full_name}. Are you ABSOLUTELY sure?"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment