1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
# frozen_string_literal: true
class Projects::ForksController < Projects::ApplicationController
include ContinueParams
include RendersMemberAccess
include RendersProjectsList
include Gitlab::Utils::StrongMemoize
# Authorize
before_action :disable_query_limiting, only: [:create]
before_action :require_non_empty_project
before_action :authorize_read_code!
before_action :authenticate_user!, only: [:new, :create]
before_action :authorize_fork_project!, except: [:index]
before_action :authorize_fork_namespace!, only: [:create]
feature_category :source_code_management
urgency :low, [:index]
def index
@sort = forks_params[:sort]
@total_forks_count = project.forks.size
@public_forks_count = project.forks.public_only.size
@private_forks_count = @total_forks_count - project.forks.public_and_internal_only.size
@internal_forks_count = @total_forks_count - @public_forks_count - @private_forks_count
@forks = load_forks.page(forks_params[:page])
prepare_projects_for_rendering(@forks)
respond_to do |format|
format.html
format.json do
render json: {
html: view_to_html_string("projects/forks/_projects", projects: @forks)
}
end
end
end
def new
respond_to do |format|
format.html do
@own_namespace = current_user.namespace if can_fork_to?(current_user.namespace)
@project = project
end
format.json do
namespaces = load_namespaces_with_associations - [project.namespace]
namespaces = [current_user.namespace] + namespaces if can_fork_to?(current_user.namespace)
render json: {
namespaces: ForkNamespaceSerializer.new.represent(
namespaces,
project: project,
current_user: current_user,
memberships: memberships_hash,
forked_projects: forked_projects_by_namespace(namespaces)
)
}
end
end
end
# rubocop: disable CodeReuse/ActiveRecord
def create
@forked_project = fork_namespace.projects.find_by(path: project.path)
@forked_project = nil unless @forked_project && @forked_project.forked_from_project == project
@forked_project ||= fork_service.execute
if !@forked_project.saved? || !@forked_project.forked?
render :error
elsif @forked_project.import_in_progress?
redirect_to project_import_path(@forked_project, continue: continue_params)
elsif continue_params[:to]
redirect_to continue_params[:to], notice: continue_params[:notice]
else
redirect_to project_path(@forked_project), notice: "The project '#{@forked_project.name}' was successfully forked."
end
end
private
def can_fork_to?(namespace)
ForkTargetsFinder.new(@project, current_user).execute.id_in(current_user.namespace).any?
end
def load_forks
forks = ForkProjectsFinder.new(
project,
params: forks_params.merge(search: forks_params[:filter_projects]),
current_user: current_user
).execute
forks.includes(:route, :creator, :group, :topics, namespace: [:route, :owner])
end
def fork_service
strong_memoize(:fork_service) do
::Projects::ForkService.new(project, current_user, fork_params)
end
end
def fork_namespace
strong_memoize(:fork_namespace) do
Namespace.find(params[:namespace_key]) if params[:namespace_key].present?
end
end
def forks_params
params.permit(:filter_projects, :sort, :page)
end
def fork_params
params.permit(:path, :name, :description, :visibility).tap do |param|
param[:namespace] = fork_namespace
end
end
def authorize_fork_namespace!
access_denied! unless fork_namespace && fork_service.valid_fork_target?
end
def disable_query_limiting
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20783')
end
def load_namespaces_with_associations
@load_namespaces_with_associations ||= fork_service.valid_fork_targets(only_groups: true).preload(:route)
end
def memberships_hash
current_user.members.where(source: load_namespaces_with_associations).index_by(&:source_id)
end
def forked_projects_by_namespace(namespaces)
project.forks.where(namespace: namespaces).includes(:namespace).index_by(&:namespace_id)
end
end
Projects::ForksController.prepend_mod_with('Projects::ForksController')
|