Commit 1248d0f3 authored by huzaifaiftikhar1's avatar huzaifaiftikhar1

Allow project owners to list & restore their projects pending deletion

Display the "Deleted projects" tab to all the users if the instance is
on a premium plan or above. Instance administrators will be able to list
all the projects that are pending deletion.

Changelog: added
EE: true
parent f7751d32
......@@ -103,7 +103,7 @@ module Nav
shortcut_href: dashboard_projects_path,
**projects_menu_item_attrs
)
builder.add_view(PROJECTS_VIEW, container_view_props(namespace: 'projects', current_item: current_item, submenu: projects_submenu))
builder.add_view(PROJECTS_VIEW, container_view_props(namespace: 'projects', current_item: current_item, submenu: projects_submenu(::Gitlab::Nav::TopNavMenuBuilder.new)))
end
if dashboard_nav_link?(:groups)
......@@ -252,9 +252,8 @@ module Nav
}
end
def projects_submenu
def projects_submenu(builder)
# These project links come from `app/views/layouts/nav/projects_dropdown/_show.html.haml`
builder = ::Gitlab::Nav::TopNavMenuBuilder.new
builder.add_primary_menu_item(id: 'your', title: _('Your projects'), href: dashboard_projects_path)
builder.add_primary_menu_item(id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path)
builder.add_primary_menu_item(id: 'explore', title: _('Explore projects'), href: explore_root_path)
......
......@@ -12,7 +12,7 @@ module EE
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def removed
@projects = load_projects(params.merge(aimed_for_deletion: true))
@projects = load_projects(params.merge(finder_params_for_removed))
respond_to do |format|
format.html
......@@ -35,13 +35,26 @@ module EE
override :load_projects
def load_projects(finder_params)
@removed_projects_count = ::ProjectsFinder.new(params: { aimed_for_deletion: true }, current_user: current_user).execute # rubocop:disable Gitlab/ModuleWithInstanceVariables
@removed_projects_count = ::ProjectsFinder.new(params: finder_params_for_removed, current_user: current_user).execute # rubocop:disable Gitlab/ModuleWithInstanceVariables
super
end
def check_adjourned_deletion_listing_availability
return render_404 unless can?(current_user, :list_removable_projects)
return render_404 unless License.feature_available?(:adjourned_deletion_for_projects_and_groups)
end
def finder_params_for_removed
finder_params = { aimed_for_deletion: true }
unless current_user.can_admin_all_resources?
# only list projects with at least owner access if the user is not an admin
finder_params[:min_access_level] = ::Gitlab::Access::OWNER
# only list projects that belongs to a group with premium or above plan
finder_params[:plans] = (::Plan.paid_plans - [::Plan::BRONZE])
end
finder_params
end
end
end
......
......@@ -50,6 +50,15 @@ module EE
)
end
end
override :projects_submenu
def projects_submenu(builder)
if License.feature_available?(:adjourned_deletion_for_projects_and_groups)
builder.add_primary_menu_item(id: 'deleted', title: _('Deleted projects'), href: removed_dashboard_projects_path)
end
super
end
end
end
end
......@@ -39,6 +39,10 @@ module EE
EE_DEFAULT_PLANS
end
def paid_plans
PAID_HOSTED_PLANS
end
# This always returns an object if running on GitLab.com
def free
return unless ::Gitlab.com?
......
......@@ -13,10 +13,6 @@ module EE
License.feature_available?(:pages_size_limit)
end
condition(:adjourned_project_deletion_available) do
License.feature_available?(:adjourned_deletion_for_projects_and_groups)
end
condition(:export_user_permissions_available) do
::License.feature_available?(:export_user_permissions)
end
......@@ -57,10 +53,6 @@ module EE
prevent :create_group_with_default_branch_protection
end
rule { admin & adjourned_project_deletion_available }.policy do
enable :list_removable_projects
end
rule { export_user_permissions_available & admin }.enable :export_user_permissions
rule { can?(:create_group) }.enable :create_group_via_api
......
- if can?(current_user, :list_removable_projects)
- if License.feature_available?(:adjourned_deletion_for_projects_and_groups)
= gl_tab_link_to removed_dashboard_projects_path, { data: { placement: 'right' } } do
= _("Pending deletion")
= gl_tab_counter_badge(limited_counter_with_delimiter(removed_projects_count))
- if current_user&.admin? && scheduled_for_deletion?(project)
- if License.feature_available?(:adjourned_deletion_for_projects_and_groups) && scheduled_for_deletion?(project)
.gl-display-flex.gl-align-items-center.gl-flex-wrap.project-title
%span.small
= _("Marked For Deletion At - %{deletion_time}") % { deletion_time: project.marked_for_deletion_at.strftime(Date::DATE_FORMATS[:medium]) }
......
......@@ -51,8 +51,39 @@ RSpec.describe Dashboard::ProjectsController do
end
end
context 'for non-admin users' do
it_behaves_like 'returns not found'
context 'for non-admin users', :saas do
let_it_be(:non_admin_user) { create(:user) }
let_it_be(:ultimate_group) { create(:group_with_plan, plan: :ultimate_plan) }
let_it_be(:premium_group) { create(:group_with_plan, plan: :premium_plan) }
let_it_be(:no_plan_group) { create(:group_with_plan, plan: nil) }
let_it_be(:ultimate_project) { create(:project, :archived, creator: non_admin_user, marked_for_deletion_at: 3.days.ago, namespace: ultimate_group) }
let_it_be(:premium_project) { create(:project, :archived, creator: non_admin_user, marked_for_deletion_at: 3.days.ago, namespace: premium_group) }
let_it_be(:no_plan_project) { create(:project, :archived, creator: non_admin_user, marked_for_deletion_at: 3.days.ago, namespace: no_plan_group) }
before do
sign_in(non_admin_user)
ultimate_group.add_owner(non_admin_user)
premium_group.add_owner(non_admin_user)
no_plan_group.add_owner(non_admin_user)
end
it 'returns success' do
subject
expect(response).to have_gitlab_http_status(:ok)
end
it 'paginates the records' do
subject
expect(assigns(:projects).count).to eq(1)
end
it 'accounts total removable projects owned by the user on premium or above plan' do
subject
expect(assigns(:removed_projects_count).count).to eq(2)
end
end
end
......
......@@ -199,48 +199,6 @@ RSpec.describe GlobalPolicy do
end
end
describe 'list_removable_projects' do
context 'when user is an admin', :enable_admin_mode do
let_it_be(:current_user) { admin }
before do
stub_licensed_features(adjourned_deletion_for_projects_and_groups: licensed?)
end
context 'when licensed feature is enabled' do
let(:licensed?) { true }
it { is_expected.to be_allowed(:list_removable_projects) }
end
context 'when licensed feature is enabled' do
let(:licensed?) { false }
it { is_expected.to be_disallowed(:list_removable_projects) }
end
end
context 'when user is a normal user' do
let_it_be(:current_user) { create(:user) }
before do
stub_licensed_features(adjourned_deletion_for_projects_and_groups: licensed?)
end
context 'when licensed feature is enabled' do
let(:licensed?) { true }
it { is_expected.to be_disallowed(:list_removable_projects) }
end
context 'when licensed feature is enabled' do
let(:licensed?) { false }
it { is_expected.to be_disallowed(:list_removable_projects) }
end
end
end
describe ':export_user_permissions', :enable_admin_mode do
using RSpec::Parameterized::TableSyntax
......
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