Skip to content

Commit d9c82d6

Browse files
author
Douwe Maan
committed
Automatically fork a project when not allowed to edit a file.
1 parent 110a5f9 commit d9c82d6

29 files changed

+452
-273
lines changed

app/assets/javascripts/blob/blob_file_dropzone.js.coffee

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class @BlobFileDropzone
3535
return
3636

3737
this.on 'sending', (file, xhr, formData) ->
38-
formData.append('new_branch', form.find('.js-new-branch').val())
38+
formData.append('target_branch', form.find('.js-target-branch').val())
3939
formData.append('create_merge_request', form.find('.js-create-merge-request').val())
4040
formData.append('commit_message', form.find('.js-commit-message').val())
4141
return

app/assets/javascripts/new_commit_form.js.coffee

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
class @NewCommitForm
22
constructor: (form) ->
3-
@newBranch = form.find('.js-new-branch')
3+
@newBranch = form.find('.js-target-branch')
44
@originalBranch = form.find('.js-original-branch')
55
@createMergeRequest = form.find('.js-create-merge-request')
66
@createMergeRequestContainer = form.find('.js-create-merge-request-container')
+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
module CreatesCommit
2+
extend ActiveSupport::Concern
3+
4+
def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil)
5+
set_commit_variables
6+
7+
commit_params = @commit_params.merge(
8+
source_project: @project,
9+
source_branch: @ref,
10+
target_branch: @target_branch
11+
)
12+
13+
result = service.new(@tree_edit_project, current_user, commit_params).execute
14+
15+
if result[:status] == :success
16+
flash[:notice] = success_notice || "Your changes have been successfully committed."
17+
18+
if create_merge_request?
19+
success_path = new_merge_request_path
20+
target = different_project? ? "project" : "branch"
21+
flash[:notice] << " You can now submit a merge request to get this change into the original #{target}."
22+
end
23+
24+
respond_to do |format|
25+
format.html { redirect_to success_path }
26+
format.json { render json: { message: "success", filePath: success_path } }
27+
end
28+
else
29+
flash[:alert] = result[:message]
30+
respond_to do |format|
31+
format.html do
32+
if failure_view
33+
render failure_view
34+
else
35+
redirect_to failure_path
36+
end
37+
end
38+
format.json { render json: { message: "failed", filePath: failure_path } }
39+
end
40+
end
41+
end
42+
43+
def authorize_edit_tree!
44+
return if can?(current_user, :push_code, project)
45+
return if current_user && current_user.already_forked?(project)
46+
47+
access_denied!
48+
end
49+
50+
private
51+
52+
def new_merge_request_path
53+
new_namespace_project_merge_request_path(
54+
@mr_source_project.namespace,
55+
@mr_source_project,
56+
merge_request: {
57+
source_project_id: @mr_source_project.id,
58+
target_project_id: @mr_target_project.id,
59+
source_branch: @mr_source_branch,
60+
target_branch: @mr_target_branch
61+
}
62+
)
63+
end
64+
65+
def different_project?
66+
@mr_source_project != @mr_target_project
67+
end
68+
69+
def different_branch?
70+
@mr_source_branch != @mr_target_branch || different_project?
71+
end
72+
73+
def create_merge_request?
74+
params[:create_merge_request].present? && different_branch?
75+
end
76+
77+
def set_commit_variables
78+
@mr_source_branch = @target_branch
79+
80+
if can?(current_user, :push_code, @project)
81+
# Edit file in this project
82+
@tree_edit_project = @project
83+
@mr_source_project = @project
84+
85+
if @project.forked?
86+
# Merge request from this project to fork origin
87+
@mr_target_project = @project.forked_from_project
88+
@mr_target_branch = @mr_target_project.repository.root_ref
89+
else
90+
# Merge request to this project
91+
@mr_target_project = @project
92+
@mr_target_branch = @ref
93+
end
94+
else
95+
# Edit file in fork
96+
@tree_edit_project = current_user.fork_of(@project)
97+
# Merge request from fork to this project
98+
@mr_source_project = @tree_edit_project
99+
@mr_target_project = @project
100+
@mr_target_branch = @mr_target_project.repository.root_ref
101+
end
102+
end
103+
end

app/controllers/concerns/creates_merge_request_for_commit.rb

-28
This file was deleted.

app/controllers/projects/blob_controller.rb

+17-77
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
# Controller for viewing a file's blame
22
class Projects::BlobController < Projects::ApplicationController
33
include ExtractsPath
4-
include CreatesMergeRequestForCommit
4+
include CreatesCommit
55
include ActionView::Helpers::SanitizeHelper
66

77
# Raised when given an invalid file path
88
class InvalidPathError < StandardError; end
99

1010
before_action :require_non_empty_project, except: [:new, :create]
1111
before_action :authorize_download_code!
12-
before_action :authorize_push_code!, only: [:destroy, :create]
12+
before_action :authorize_edit_tree!, only: [:new, :create, :edit, :update, :destroy]
1313
before_action :assign_blob_vars
1414
before_action :commit, except: [:new, :create]
1515
before_action :blob, except: [:new, :create]
1616
before_action :from_merge_request, only: [:edit, :update]
1717
before_action :require_branch_head, only: [:edit, :update]
1818
before_action :editor_variables, except: [:show, :preview, :diff]
19-
before_action :after_edit_path, only: [:edit, :update]
2019

2120
def new
2221
commit unless @repository.empty?
2322
end
2423

2524
def create
26-
create_commit(Files::CreateService, success_path: after_create_path,
25+
create_commit(Files::CreateService, success_notice: "The file has been successfully created.",
26+
success_path: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)),
2727
failure_view: :new,
2828
failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
2929
end
@@ -36,6 +36,14 @@ def edit
3636
end
3737

3838
def update
39+
after_edit_path =
40+
if from_merge_request && @target_branch == @ref
41+
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
42+
"#file-path-#{hexdigest(@path)}"
43+
else
44+
namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
45+
end
46+
3947
create_commit(Files::UpdateService, success_path: after_edit_path,
4048
failure_view: :edit,
4149
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
@@ -50,15 +58,10 @@ def preview
5058
end
5159

5260
def destroy
53-
result = Files::DeleteService.new(@project, current_user, @commit_params).execute
54-
55-
if result[:status] == :success
56-
flash[:notice] = "Your changes have been successfully committed"
57-
redirect_to after_destroy_path
58-
else
59-
flash[:alert] = result[:message]
60-
render :show
61-
end
61+
create_commit(Files::DeleteService, success_notice: "The file has been successfully deleted.",
62+
success_path: namespace_project_tree_path(@project.namespace, @project, @target_branch),
63+
failure_view: :show,
64+
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
6265
end
6366

6467
def diff
@@ -108,74 +111,13 @@ def assign_blob_vars
108111
render_404
109112
end
110113

111-
def create_commit(service, success_path:, failure_view:, failure_path:)
112-
result = service.new(@project, current_user, @commit_params).execute
113-
114-
if result[:status] == :success
115-
flash[:notice] = "Your changes have been successfully committed"
116-
respond_to do |format|
117-
format.html { redirect_to success_path }
118-
format.json { render json: { message: "success", filePath: success_path } }
119-
end
120-
else
121-
flash[:alert] = result[:message]
122-
respond_to do |format|
123-
format.html { render failure_view }
124-
format.json { render json: { message: "failed", filePath: failure_path } }
125-
end
126-
end
127-
end
128-
129-
def after_create_path
130-
@after_create_path ||=
131-
if create_merge_request?
132-
new_merge_request_path
133-
else
134-
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @file_path))
135-
end
136-
end
137-
138-
def after_edit_path
139-
@after_edit_path ||=
140-
if create_merge_request?
141-
new_merge_request_path
142-
elsif from_merge_request && @new_branch == @ref
143-
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
144-
"#file-path-#{hexdigest(@path)}"
145-
else
146-
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @path))
147-
end
148-
end
149-
150-
def after_destroy_path
151-
@after_destroy_path ||=
152-
if create_merge_request?
153-
new_merge_request_path
154-
else
155-
namespace_project_tree_path(@project.namespace, @project, @new_branch)
156-
end
157-
end
158-
159114
def from_merge_request
160115
# If blob edit was initiated from merge request page
161116
@from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
162117
end
163118

164-
def sanitized_new_branch_name
165-
sanitize(strip_tags(params[:new_branch]))
166-
end
167-
168119
def editor_variables
169-
@current_branch = @ref
170-
171-
@new_branch =
172-
if params[:new_branch].present?
173-
sanitized_new_branch_name
174-
elsif ::Gitlab::GitAccess.new(current_user, @project).can_push_to_branch?(@ref)
175-
@ref
176-
else
177-
@repository.next_patch_branch
178-
end
120+
@target_branch = params[:target_branch]
179121

180122
@file_path =
181123
if action_name.to_s == 'create'
@@ -194,8 +136,6 @@ def editor_variables
194136

195137
@commit_params = {
196138
file_path: @file_path,
197-
current_branch: @current_branch,
198-
target_branch: @new_branch,
199139
commit_message: params[:commit_message],
200140
file_content: params[:content],
201141
file_content_encoding: params[:encoding]

app/controllers/projects/forks_controller.rb

+14-5
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,25 @@ def create
1313
@forked_project = ::Projects::ForkService.new(project, current_user, namespace: namespace).execute
1414

1515
if @forked_project.saved? && @forked_project.forked?
16+
continue_params[:notice] ||= "The project was successfully forked."
17+
1618
if @forked_project.import_in_progress?
17-
redirect_to namespace_project_import_path(@forked_project.namespace, @forked_project)
19+
redirect_to namespace_project_import_path(@forked_project.namespace, @forked_project, continue: continue_params)
1820
else
19-
redirect_to(
20-
namespace_project_path(@forked_project.namespace, @forked_project),
21-
notice: 'Project was successfully forked.'
22-
)
21+
if continue_params
22+
redirect_to continue_params[:to], notice: continue_params[:notice]
23+
else
24+
redirect_to namespace_project_path(@forked_project.namespace, @forked_project)
25+
end
2326
end
2427
else
2528
render :error
2629
end
2730
end
31+
32+
private
33+
34+
def continue_params
35+
params[:continue].permit(:to, :notice, :notice_now)
36+
end
2837
end

app/controllers/projects/imports_controller.rb

+17-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
class Projects::ImportsController < Projects::ApplicationController
22
# Authorize
33
before_action :authorize_admin_project!
4-
before_action :require_no_repo
4+
before_action :require_no_repo, except: :show
55
before_action :redirect_if_progress, except: :show
66

77
def new
@@ -24,21 +24,31 @@ def create
2424
end
2525

2626
def show
27-
unless @project.import_in_progress?
28-
if @project.import_finished?
29-
redirect_to(project_path(@project)) and return
27+
if @project.repository_exists? || @project.import_finished?
28+
if continue_params
29+
redirect_to continue_params[:to], notice: continue_params[:notice]
3030
else
31-
redirect_to(new_namespace_project_import_path(@project.namespace,
32-
@project)) and return
31+
redirect_to project_path(@project)
3332
end
33+
elsif @project.import_failed?
34+
redirect_to new_namespace_project_import_path(@project.namespace, @project)
35+
else
36+
if continue_params && continue_params[:notice_now]
37+
flash.now[:notice] = continue_params[:notice_now]
38+
end
39+
# Render
3440
end
3541
end
3642

3743
private
3844

45+
def continue_params
46+
@continue_params ||= params[:continue].permit(:to, :notice, :notice_now)
47+
end
48+
3949
def require_no_repo
4050
if @project.repository_exists? && !@project.import_in_progress?
41-
redirect_to(namespace_project_path(@project.namespace, @project)) and return
51+
redirect_to(namespace_project_path(@project.namespace, @project))
4252
end
4353
end
4454

0 commit comments

Comments
 (0)