1# frozen_string_literal: true
2
3class Dashboard::ProjectsController < Dashboard::ApplicationController
4  include ParamsBackwardCompatibility
5  include RendersMemberAccess
6  include RendersProjectsList
7  include SortingHelper
8  include SortingPreference
9  include FiltersEvents
10
11  prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
12  before_action :set_non_archived_param, only: [:index, :starred]
13  before_action :set_sorting
14  before_action :projects, only: [:index]
15  skip_cross_project_access_check :index, :starred
16
17  feature_category :projects
18
19  def index
20    respond_to do |format|
21      format.html do
22        render_projects
23      end
24      format.atom do
25        load_events
26        render layout: 'xml.atom'
27      end
28      format.json do
29        render json: {
30          html: view_to_html_string("dashboard/projects/_projects", projects: @projects)
31        }
32      end
33    end
34  end
35
36  # rubocop: disable CodeReuse/ActiveRecord
37  def starred
38    @projects = load_projects(params.merge(starred: true))
39      .includes(:forked_from_project, :topics)
40
41    @groups = []
42
43    respond_to do |format|
44      format.html
45      format.json do
46        render json: {
47          html: view_to_html_string("dashboard/projects/_projects", projects: @projects)
48        }
49      end
50    end
51  end
52  # rubocop: enable CodeReuse/ActiveRecord
53
54  private
55
56  def projects
57    @projects ||= load_projects(params.merge(non_public: true))
58  end
59
60  def render_projects
61    # n+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/40260
62    Gitlab::GitalyClient.allow_n_plus_1_calls do
63      render
64    end
65  end
66
67  def load_projects(finder_params)
68    @total_user_projects_count = ProjectsFinder.new(params: { non_public: true, without_deleted: true }, current_user: current_user).execute
69    @total_starred_projects_count = ProjectsFinder.new(params: { starred: true, without_deleted: true }, current_user: current_user).execute
70
71    finder_params[:use_cte] = true if use_cte_for_finder?
72    finder_params[:without_deleted] = true
73
74    projects = ProjectsFinder.new(params: finder_params, current_user: current_user).execute
75
76    projects = preload_associations(projects)
77    projects = projects.page(finder_params[:page])
78
79    prepare_projects_for_rendering(projects)
80  end
81
82  # rubocop: disable CodeReuse/ActiveRecord
83  def preload_associations(projects)
84    projects.includes(:route, :creator, :group, :topics, namespace: [:route, :owner]).preload(:project_feature)
85  end
86  # rubocop: enable CodeReuse/ActiveRecord
87
88  def use_cte_for_finder?
89    # The starred action loads public projects, which causes the CTE to be less efficient
90    action_name == 'index'
91  end
92
93  def load_events
94    projects = ProjectsFinder
95                .new(params: params.merge(non_public: true, without_deleted: true), current_user: current_user)
96                .execute
97
98    @events = EventCollection
99      .new(projects, offset: params[:offset].to_i, filter: event_filter)
100      .to_a
101
102    Events::RenderService.new(current_user).execute(@events, atom_request: request.format.atom?)
103  end
104
105  def set_sorting
106    params[:sort] = set_sort_order
107    @sort = params[:sort]
108  end
109
110  def default_sort_order
111    sort_value_name
112  end
113
114  def sorting_field
115    Project::SORTING_PREFERENCE_FIELD
116  end
117end
118
119Dashboard::ProjectsController.prepend_mod_with('Dashboard::ProjectsController')
120