gitlab-ce/app/helpers/namespaces/deletable_helper.rb

266 lines
9.9 KiB
Ruby

# frozen_string_literal: true
module Namespaces
module DeletableHelper
def permanent_deletion_date_formatted(container_or_date = Date.current, format: '%F')
date =
if container_or_date.respond_to?(:self_deletion_scheduled_deletion_created_on)
container_or_date.self_deletion_scheduled_deletion_created_on
else
container_or_date
end
return unless date.is_a?(Date) || date.is_a?(Time)
::Gitlab::CurrentSettings.deletion_adjourned_period.days.since(date).strftime(format)
end
def deletion_in_progress_or_scheduled_in_hierarchy_chain?(namespace)
return false unless namespace.respond_to?(:deletion_in_progress_or_scheduled_in_hierarchy_chain?)
namespace.deletion_in_progress_or_scheduled_in_hierarchy_chain?
end
def self_or_ancestors_deletion_in_progress_or_scheduled_message(namespace)
_self_deletion_in_progress_message(namespace) || _deletion_scheduled_in_hierarchy_chain_message(namespace)
end
def _self_deletion_in_progress_message(namespace)
return unless namespace.self_deletion_in_progress?
messages = {
group: _('This group and its subgroups are being deleted.'),
project: _('This project is being deleted. Repository and other project resources are read-only.')
}
_message_for_namespace(namespace, messages)
end
def _deletion_scheduled_in_hierarchy_chain_message(namespace)
if namespace.self_deletion_scheduled?
_self_deletion_scheduled_message(namespace)
else
_parent_deletion_scheduled_message(namespace)
end
end
def _self_deletion_scheduled_message(namespace)
date = permanent_deletion_date_formatted(namespace)
messages = {
group: _('This group and its subgroups and projects are pending deletion, and will be deleted on %{date}.'),
project: _('This project is pending deletion, and will be deleted on %{date}. Repository and other project ' \
'resources are read-only.')
}
safe_format(
_message_for_namespace(namespace, messages),
date: tag.strong(date)
)
end
def _parent_deletion_scheduled_message(namespace)
namespace_pending_deletion = namespace.first_scheduled_for_deletion_in_hierarchy_chain
date = permanent_deletion_date_formatted(namespace_pending_deletion)
messages = {
group: _('This group will be deleted on %{date} because its parent group is ' \
'scheduled for deletion.'),
project: _('This project will be deleted on %{date} because its parent group is ' \
'scheduled for deletion.')
}
safe_format(
_message_for_namespace(namespace, messages),
date: tag.strong(date)
)
end
def delete_delayed_namespace_message(namespace)
messages = {
group: _('This action will place this group, including its subgroups and projects, ' \
'in a pending deletion state for %{deletion_adjourned_period} days, ' \
'and delete it permanently on %{date}.'),
project: _('This action will place this project, including all its resources, ' \
'in a pending deletion state for %{deletion_adjourned_period} days, ' \
'and delete it permanently on %{date}.')
}
safe_format(
_message_for_namespace(namespace, messages),
deletion_adjourned_period: namespace.deletion_adjourned_period,
date: tag.strong(permanent_deletion_date_formatted)
)
end
def delete_immediately_namespace_scheduled_for_deletion_message(namespace)
messages = {
group: _('This group is scheduled for deletion on %{date}. ' \
'This action will permanently delete this group, ' \
'including its subgroups and projects, %{strongOpen}immediately%{strongClose}. ' \
'This action cannot be undone.'),
project: _('This project is scheduled for deletion on %{date}. ' \
'This action will permanently delete this project, ' \
'including all its resources, %{strongOpen}immediately%{strongClose}. ' \
'This action cannot be undone.')
}
safe_format(
_message_for_namespace(namespace, messages),
date: tag.strong(permanent_deletion_date_formatted(namespace)),
strongOpen: '<strong>'.html_safe,
strongClose: '</strong>'.html_safe
)
end
def group_confirm_modal_data(
group:,
remove_form_id: nil,
permanently_remove: false,
button_text: nil,
has_security_policy_project: false)
{
remove_form_id: remove_form_id,
button_text: button_text.nil? ? _('Delete group') : button_text,
button_testid: 'remove-group-button',
disabled: (group.linked_to_subscription? || has_security_policy_project).to_s,
confirm_danger_message: confirm_remove_group_message(group, permanently_remove),
phrase: group.full_path,
html_confirmation_message: 'true'
}
end
def confirm_remove_group_message(group, permanently_remove)
return _permanently_delete_group_message(group) if permanently_remove || group.self_deletion_scheduled?
safe_format(
_("The contents of this group, its subgroups and projects will be permanently deleted after " \
"%{deletion_adjourned_period} days on %{date}. After this point, your data cannot be recovered."),
deletion_adjourned_period: group.deletion_adjourned_period,
date: tag.strong(permanent_deletion_date_formatted)
)
end
def project_delete_delayed_button_data(project, button_text = nil)
_project_delete_button_shared_data(project, button_text).merge({
restore_help_path: help_page_path('user/project/working_with_projects.md', anchor: 'restore-a-project'),
delayed_deletion_date: permanent_deletion_date_formatted,
form_path: project_path(project)
})
end
def project_delete_immediately_button_data(project, button_text = nil)
_project_delete_button_shared_data(project, button_text).merge({
form_path: project_path(project, permanently_delete: true)
})
end
def restore_namespace_title(namespace)
messages = {
group: _('Restore group'),
project: _('Restore project')
}
_message_for_namespace(namespace, messages)
end
def restore_namespace_path(namespace)
paths = {
group: ->(namespace) { group_restore_path(namespace) },
project: ->(namespace) { namespace_project_restore_path(namespace.parent, namespace) }
}
_message_for_namespace(namespace, paths)[namespace]
end
def restore_namespace_scheduled_for_deletion_message(namespace)
messages = {
group: _("This group has been scheduled for deletion on %{date}. " \
"To cancel the scheduled deletion, you can restore this group, including all its resources."),
project: _("This project has been scheduled for deletion on %{date}. " \
"To cancel the scheduled deletion, you can restore this project, including all its resources.")
}
safe_format(
_message_for_namespace(namespace, messages),
date: tag.strong(permanent_deletion_date_formatted(namespace))
)
end
def _message_for_namespace(namespace, messages)
# In case `namespace` is a Presenter instance, we match on the model name instead of class name.
case namespace.model_name.name
when 'Group'
messages[:group]
when 'Project', 'Namespaces::ProjectNamespace'
messages[:project]
else
raise "Unsupported namespace type: #{namespace.class.name}"
end
end
def _permanently_delete_group_message(group)
content = ''.html_safe
content << content_tag(:span,
format(_("You are about to delete the group %{group_name}."), group_name: group.name))
content << _additional_removed_items(group)
content << _remove_group_warning
end
def _additional_removed_items(group)
relations = {
->(count) { n_('%{count} subgroup', '%{count} subgroups', count) } => group.children,
->(count) {
n_('%{count} active project', '%{count} active projects', count)
} => group.all_projects.non_archived,
->(count) {
n_('%{count} archived project', '%{count} archived projects', count)
} => group.all_projects.archived
}
counts = relations.filter_map do |i18n_proc, relation|
count = limited_counter_with_delimiter(relation, limit: 100, include_zero: false)
next unless count
content_tag(:li, format(i18n_proc[count.to_i], count: count))
end
if counts.any?
safe_join([
content_tag(:span, _(" This action will also delete:")),
content_tag(:ul, safe_join(counts))
])
else
''
end
end
def _remove_group_warning
content_tag(:p, class: 'gl-mb-0') do
safe_format(
_('After you delete a group, you %{strongOpen}cannot%{strongClose} restore it or its components.'),
strongOpen: '<strong>'.html_safe,
strongClose: '</strong>'.html_safe
)
end
end
def _project_delete_button_shared_data(project, button_text = nil)
merge_requests_count = ::Projects::AllMergeRequestsCountService.new(project).count
issues_count = ::Projects::AllIssuesCountService.new(project).count
forks_count = ::Projects::ForksCountService.new(project).count
{
confirm_phrase: delete_confirm_phrase(project),
name_with_namespace: project.name_with_namespace,
is_fork: project.forked? ? 'true' : 'false',
issues_count: number_with_delimiter(issues_count),
merge_requests_count: number_with_delimiter(merge_requests_count),
forks_count: number_with_delimiter(forks_count),
stars_count: number_with_delimiter(project.star_count),
button_text: button_text.presence || _('Delete project')
}
end
end
end