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 ...@@ -103,7 +103,7 @@ module Nav
shortcut_href: dashboard_projects_path, shortcut_href: dashboard_projects_path,
**projects_menu_item_attrs **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 end
if dashboard_nav_link?(:groups) if dashboard_nav_link?(:groups)
...@@ -252,9 +252,8 @@ module Nav ...@@ -252,9 +252,8 @@ module Nav
} }
end end
def projects_submenu def projects_submenu(builder)
# These project links come from `app/views/layouts/nav/projects_dropdown/_show.html.haml` # 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: '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: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path)
builder.add_primary_menu_item(id: 'explore', title: _('Explore projects'), href: explore_root_path) builder.add_primary_menu_item(id: 'explore', title: _('Explore projects'), href: explore_root_path)
......
...@@ -12,7 +12,7 @@ module EE ...@@ -12,7 +12,7 @@ module EE
# rubocop:disable Gitlab/ModuleWithInstanceVariables # rubocop:disable Gitlab/ModuleWithInstanceVariables
def removed def removed
@projects = load_projects(params.merge(aimed_for_deletion: true)) @projects = load_projects(params.merge(finder_params_for_removed))
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -35,13 +35,26 @@ module EE ...@@ -35,13 +35,26 @@ module EE
override :load_projects override :load_projects
def load_projects(finder_params) 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 super
end end
def check_adjourned_deletion_listing_availability 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 end
end end
......
...@@ -50,6 +50,15 @@ module EE ...@@ -50,6 +50,15 @@ module EE
) )
end end
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 end
end end
...@@ -39,6 +39,10 @@ module EE ...@@ -39,6 +39,10 @@ module EE
EE_DEFAULT_PLANS EE_DEFAULT_PLANS
end end
def paid_plans
PAID_HOSTED_PLANS
end
# This always returns an object if running on GitLab.com # This always returns an object if running on GitLab.com
def free def free
return unless ::Gitlab.com? return unless ::Gitlab.com?
......
...@@ -13,10 +13,6 @@ module EE ...@@ -13,10 +13,6 @@ module EE
License.feature_available?(:pages_size_limit) License.feature_available?(:pages_size_limit)
end end
condition(:adjourned_project_deletion_available) do
License.feature_available?(:adjourned_deletion_for_projects_and_groups)
end
condition(:export_user_permissions_available) do condition(:export_user_permissions_available) do
::License.feature_available?(:export_user_permissions) ::License.feature_available?(:export_user_permissions)
end end
...@@ -57,10 +53,6 @@ module EE ...@@ -57,10 +53,6 @@ module EE
prevent :create_group_with_default_branch_protection prevent :create_group_with_default_branch_protection
end 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 { export_user_permissions_available & admin }.enable :export_user_permissions
rule { can?(:create_group) }.enable :create_group_via_api 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 = gl_tab_link_to removed_dashboard_projects_path, { data: { placement: 'right' } } do
= _("Pending deletion") = _("Pending deletion")
= gl_tab_counter_badge(limited_counter_with_delimiter(removed_projects_count)) = 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 .gl-display-flex.gl-align-items-center.gl-flex-wrap.project-title
%span.small %span.small
= _("Marked For Deletion At - %{deletion_time}") % { deletion_time: project.marked_for_deletion_at.strftime(Date::DATE_FORMATS[:medium]) } = _("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 ...@@ -51,8 +51,39 @@ RSpec.describe Dashboard::ProjectsController do
end end
end end
context 'for non-admin users' do context 'for non-admin users', :saas do
it_behaves_like 'returns not found' 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
end end
......
...@@ -199,48 +199,6 @@ RSpec.describe GlobalPolicy do ...@@ -199,48 +199,6 @@ RSpec.describe GlobalPolicy do
end end
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 describe ':export_user_permissions', :enable_admin_mode do
using RSpec::Parameterized::TableSyntax 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