Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
heptapod
heptapod
Commits
adc7725740ad
Commit
d9c82d67
authored
Dec 18, 2015
by
Douwe Maan
Browse files
Automatically fork a project when not allowed to edit a file.
parent
a6816a59d465
Changes
29
Hide whitespace changes
Inline
Side-by-side
app/assets/javascripts/blob/blob_file_dropzone.js.coffee
View file @
adc77257
...
...
@@ -35,7 +35,7 @@ class @BlobFileDropzone
return
this
.
on
'sending'
,
(
file
,
xhr
,
formData
)
->
formData
.
append
(
'
new
_branch'
,
form
.
find
(
'.js-
new
-branch'
).
val
())
formData
.
append
(
'
target
_branch'
,
form
.
find
(
'.js-
target
-branch'
).
val
())
formData
.
append
(
'create_merge_request'
,
form
.
find
(
'.js-create-merge-request'
).
val
())
formData
.
append
(
'commit_message'
,
form
.
find
(
'.js-commit-message'
).
val
())
return
...
...
app/assets/javascripts/new_commit_form.js.coffee
View file @
adc77257
class
@
NewCommitForm
constructor
:
(
form
)
->
@
newBranch
=
form
.
find
(
'.js-
new
-branch'
)
@
newBranch
=
form
.
find
(
'.js-
target
-branch'
)
@
originalBranch
=
form
.
find
(
'.js-original-branch'
)
@
createMergeRequest
=
form
.
find
(
'.js-create-merge-request'
)
@
createMergeRequestContainer
=
form
.
find
(
'.js-create-merge-request-container'
)
...
...
app/controllers/concerns/creates_commit.rb
0 → 100644
View file @
adc77257
module
CreatesCommit
extend
ActiveSupport
::
Concern
def
create_commit
(
service
,
success_path
:,
failure_path
:,
failure_view:
nil
,
success_notice:
nil
)
set_commit_variables
commit_params
=
@commit_params
.
merge
(
source_project:
@project
,
source_branch:
@ref
,
target_branch:
@target_branch
)
result
=
service
.
new
(
@tree_edit_project
,
current_user
,
commit_params
).
execute
if
result
[
:status
]
==
:success
flash
[
:notice
]
=
success_notice
||
"Your changes have been successfully committed."
if
create_merge_request?
success_path
=
new_merge_request_path
target
=
different_project?
?
"project"
:
"branch"
flash
[
:notice
]
<<
" You can now submit a merge request to get this change into the original
#{
target
}
."
end
respond_to
do
|
format
|
format
.
html
{
redirect_to
success_path
}
format
.
json
{
render
json:
{
message:
"success"
,
filePath:
success_path
}
}
end
else
flash
[
:alert
]
=
result
[
:message
]
respond_to
do
|
format
|
format
.
html
do
if
failure_view
render
failure_view
else
redirect_to
failure_path
end
end
format
.
json
{
render
json:
{
message:
"failed"
,
filePath:
failure_path
}
}
end
end
end
def
authorize_edit_tree!
return
if
can?
(
current_user
,
:push_code
,
project
)
return
if
current_user
&&
current_user
.
already_forked?
(
project
)
access_denied!
end
private
def
new_merge_request_path
new_namespace_project_merge_request_path
(
@mr_source_project
.
namespace
,
@mr_source_project
,
merge_request:
{
source_project_id:
@mr_source_project
.
id
,
target_project_id:
@mr_target_project
.
id
,
source_branch:
@mr_source_branch
,
target_branch:
@mr_target_branch
}
)
end
def
different_project?
@mr_source_project
!=
@mr_target_project
end
def
different_branch?
@mr_source_branch
!=
@mr_target_branch
||
different_project?
end
def
create_merge_request?
params
[
:create_merge_request
].
present?
&&
different_branch?
end
def
set_commit_variables
@mr_source_branch
=
@target_branch
if
can?
(
current_user
,
:push_code
,
@project
)
# Edit file in this project
@tree_edit_project
=
@project
@mr_source_project
=
@project
if
@project
.
forked?
# Merge request from this project to fork origin
@mr_target_project
=
@project
.
forked_from_project
@mr_target_branch
=
@mr_target_project
.
repository
.
root_ref
else
# Merge request to this project
@mr_target_project
=
@project
@mr_target_branch
=
@ref
end
else
# Edit file in fork
@tree_edit_project
=
current_user
.
fork_of
(
@project
)
# Merge request from fork to this project
@mr_source_project
=
@tree_edit_project
@mr_target_project
=
@project
@mr_target_branch
=
@mr_target_project
.
repository
.
root_ref
end
end
end
app/controllers/concerns/creates_merge_request_for_commit.rb
deleted
100644 → 0
View file @
a6816a59
module
CreatesMergeRequestForCommit
extend
ActiveSupport
::
Concern
def
new_merge_request_path
if
@project
.
forked?
target_project
=
@project
.
forked_from_project
||
@project
target_branch
=
target_project
.
repository
.
root_ref
else
target_project
=
@project
target_branch
=
@ref
end
new_namespace_project_merge_request_path
(
@project
.
namespace
,
@project
,
merge_request:
{
source_project_id:
@project
.
id
,
target_project_id:
target_project
.
id
,
source_branch:
@new_branch
,
target_branch:
target_branch
}
)
end
def
create_merge_request?
params
[
:create_merge_request
]
&&
@new_branch
!=
@ref
end
end
app/controllers/projects/blob_controller.rb
View file @
adc77257
# Controller for viewing a file's blame
class
Projects::BlobController
<
Projects
::
ApplicationController
include
ExtractsPath
include
Creates
MergeRequestFor
Commit
include
CreatesCommit
include
ActionView
::
Helpers
::
SanitizeHelper
# Raised when given an invalid file path
...
...
@@ -9,21 +9,21 @@ class InvalidPathError < StandardError; end
before_action
:require_non_empty_project
,
except:
[
:new
,
:create
]
before_action
:authorize_download_code!
before_action
:authorize_
push_cod
e!
,
only:
[
:
destroy
,
:create
]
before_action
:authorize_
edit_tre
e!
,
only:
[
:
new
,
:create
,
:edit
,
:update
,
:destroy
]
before_action
:assign_blob_vars
before_action
:commit
,
except:
[
:new
,
:create
]
before_action
:blob
,
except:
[
:new
,
:create
]
before_action
:from_merge_request
,
only:
[
:edit
,
:update
]
before_action
:require_branch_head
,
only:
[
:edit
,
:update
]
before_action
:editor_variables
,
except:
[
:show
,
:preview
,
:diff
]
before_action
:after_edit_path
,
only:
[
:edit
,
:update
]
def
new
commit
unless
@repository
.
empty?
end
def
create
create_commit
(
Files
::
CreateService
,
success_path:
after_create_path
,
create_commit
(
Files
::
CreateService
,
success_notice:
"The file has been successfully created."
,
success_path:
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
File
.
join
(
@target_branch
,
@file_path
)),
failure_view: :new
,
failure_path:
namespace_project_new_blob_path
(
@project
.
namespace
,
@project
,
@ref
))
end
...
...
@@ -36,6 +36,14 @@ def edit
end
def
update
after_edit_path
=
if
from_merge_request
&&
@target_branch
==
@ref
diffs_namespace_project_merge_request_path
(
from_merge_request
.
target_project
.
namespace
,
from_merge_request
.
target_project
,
from_merge_request
)
+
"#file-path-
#{
hexdigest
(
@path
)
}
"
else
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
File
.
join
(
@target_branch
,
@path
))
end
create_commit
(
Files
::
UpdateService
,
success_path:
after_edit_path
,
failure_view: :edit
,
failure_path:
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
@id
))
...
...
@@ -50,15 +58,10 @@ def preview
end
def
destroy
result
=
Files
::
DeleteService
.
new
(
@project
,
current_user
,
@commit_params
).
execute
if
result
[
:status
]
==
:success
flash
[
:notice
]
=
"Your changes have been successfully committed"
redirect_to
after_destroy_path
else
flash
[
:alert
]
=
result
[
:message
]
render
:show
end
create_commit
(
Files
::
DeleteService
,
success_notice:
"The file has been successfully deleted."
,
success_path:
namespace_project_tree_path
(
@project
.
namespace
,
@project
,
@target_branch
),
failure_view: :show
,
failure_path:
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
@id
))
end
def
diff
...
...
@@ -108,74 +111,13 @@ def assign_blob_vars
render_404
end
def
create_commit
(
service
,
success_path
:,
failure_view
:,
failure_path
:)
result
=
service
.
new
(
@project
,
current_user
,
@commit_params
).
execute
if
result
[
:status
]
==
:success
flash
[
:notice
]
=
"Your changes have been successfully committed"
respond_to
do
|
format
|
format
.
html
{
redirect_to
success_path
}
format
.
json
{
render
json:
{
message:
"success"
,
filePath:
success_path
}
}
end
else
flash
[
:alert
]
=
result
[
:message
]
respond_to
do
|
format
|
format
.
html
{
render
failure_view
}
format
.
json
{
render
json:
{
message:
"failed"
,
filePath:
failure_path
}
}
end
end
end
def
after_create_path
@after_create_path
||=
if
create_merge_request?
new_merge_request_path
else
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
File
.
join
(
@new_branch
,
@file_path
))
end
end
def
after_edit_path
@after_edit_path
||=
if
create_merge_request?
new_merge_request_path
elsif
from_merge_request
&&
@new_branch
==
@ref
diffs_namespace_project_merge_request_path
(
from_merge_request
.
target_project
.
namespace
,
from_merge_request
.
target_project
,
from_merge_request
)
+
"#file-path-
#{
hexdigest
(
@path
)
}
"
else
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
File
.
join
(
@new_branch
,
@path
))
end
end
def
after_destroy_path
@after_destroy_path
||=
if
create_merge_request?
new_merge_request_path
else
namespace_project_tree_path
(
@project
.
namespace
,
@project
,
@new_branch
)
end
end
def
from_merge_request
# If blob edit was initiated from merge request page
@from_merge_request
||=
MergeRequest
.
find_by
(
id:
params
[
:from_merge_request_id
])
end
def
sanitized_new_branch_name
sanitize
(
strip_tags
(
params
[
:new_branch
]))
end
def
editor_variables
@current_branch
=
@ref
@new_branch
=
if
params
[
:new_branch
].
present?
sanitized_new_branch_name
elsif
::
Gitlab
::
GitAccess
.
new
(
current_user
,
@project
).
can_push_to_branch?
(
@ref
)
@ref
else
@repository
.
next_patch_branch
end
@target_branch
=
params
[
:target_branch
]
@file_path
=
if
action_name
.
to_s
==
'create'
...
...
@@ -194,8 +136,6 @@ def editor_variables
@commit_params
=
{
file_path:
@file_path
,
current_branch:
@current_branch
,
target_branch:
@new_branch
,
commit_message:
params
[
:commit_message
],
file_content:
params
[
:content
],
file_content_encoding:
params
[
:encoding
]
...
...
app/controllers/projects/forks_controller.rb
View file @
adc77257
...
...
@@ -13,16 +13,25 @@ def create
@forked_project
=
::
Projects
::
ForkService
.
new
(
project
,
current_user
,
namespace:
namespace
).
execute
if
@forked_project
.
saved?
&&
@forked_project
.
forked?
continue_params
[
:notice
]
||=
"The project was successfully forked."
if
@forked_project
.
import_in_progress?
redirect_to
namespace_project_import_path
(
@forked_project
.
namespace
,
@forked_project
)
redirect_to
namespace_project_import_path
(
@forked_project
.
namespace
,
@forked_project
,
continue:
continue_params
)
else
redirect_to
(
namespace_project_path
(
@forked_project
.
namespace
,
@forked_project
),
notice:
'Project was successfully forked.'
)
if
continue_params
redirect_to
continue_params
[
:to
],
notice:
continue_params
[
:notice
]
else
redirect_to
namespace_project_path
(
@forked_project
.
namespace
,
@forked_project
)
end
end
else
render
:error
end
end
private
def
continue_params
params
[
:continue
].
permit
(
:to
,
:notice
,
:notice_now
)
end
end
app/controllers/projects/imports_controller.rb
View file @
adc77257
class
Projects::ImportsController
<
Projects
::
ApplicationController
# Authorize
before_action
:authorize_admin_project!
before_action
:require_no_repo
before_action
:require_no_repo
,
except: :show
before_action
:redirect_if_progress
,
except: :show
def
new
...
...
@@ -24,21 +24,31 @@ def create
end
def
show
unless
@project
.
import_in
_progress
?
if
@project
.
import_finished?
redirect_to
(
project_path
(
@project
))
and
return
if
@project
.
repository_exists?
||
@project
.
import_
f
in
ished
?
if
continue_params
redirect_to
continue_params
[
:to
],
notice:
continue_params
[
:notice
]
else
redirect_to
(
new_namespace_project_import_path
(
@project
.
namespace
,
@project
))
and
return
redirect_to
project_path
(
@project
)
end
elsif
@project
.
import_failed?
redirect_to
new_namespace_project_import_path
(
@project
.
namespace
,
@project
)
else
if
continue_params
&&
continue_params
[
:notice_now
]
flash
.
now
[
:notice
]
=
continue_params
[
:notice_now
]
end
# Render
end
end
private
def
continue_params
@continue_params
||=
params
[
:continue
].
permit
(
:to
,
:notice
,
:notice_now
)
end
def
require_no_repo
if
@project
.
repository_exists?
&&
!
@project
.
import_in_progress?
redirect_to
(
namespace_project_path
(
@project
.
namespace
,
@project
))
and
return
redirect_to
(
namespace_project_path
(
@project
.
namespace
,
@project
))
end
end
...
...
app/controllers/projects/tree_controller.rb
View file @
adc77257
# Controller for viewing a repository's file structure
class
Projects::TreeController
<
Projects
::
ApplicationController
include
ExtractsPath
include
Creates
MergeRequestFor
Commit
include
CreatesCommit
include
ActionView
::
Helpers
::
SanitizeHelper
before_action
:require_non_empty_project
,
except:
[
:new
,
:create
]
before_action
:assign_ref_vars
before_action
:assign_dir_vars
,
only:
[
:create_dir
]
before_action
:authorize_download_code!
before_action
:authorize_
push_cod
e!
,
only:
[
:create_dir
]
before_action
:authorize_
edit_tre
e!
,
only:
[
:create_dir
]
def
show
return
render_404
unless
@repository
.
commit
(
@ref
)
...
...
@@ -34,44 +34,20 @@ def show
def
create_dir
return
render_404
unless
@commit_params
.
values
.
all?
begin
result
=
Files
::
CreateDirService
.
new
(
@project
,
current_user
,
@commit_params
).
execute
message
=
result
[
:message
]
rescue
=>
e
message
=
e
.
to_s
end
if
result
&&
result
[
:status
]
==
:success
flash
[
:notice
]
=
"The directory has been successfully created"
respond_to
do
|
format
|
format
.
html
{
redirect_to
after_create_dir_path
}
end
else
flash
[
:alert
]
=
message
respond_to
do
|
format
|
format
.
html
{
redirect_to
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
@new_branch
)
}
end
end
create_commit
(
Files
::
CreateDirService
,
success_notice:
"The directory has been successfully created."
,
success_path:
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
File
.
join
(
@target_branch
,
@dir_name
)),
failure_path:
namespace_project_tree_path
(
@project
.
namespace
,
@project
,
@ref
))
end
private
def
assign_dir_vars
@new_branch
=
params
[
:new_branch
].
present?
?
sanitize
(
strip_tags
(
params
[
:new_branch
]))
:
@ref
@target_branch
=
params
[
:target_branch
]
@dir_name
=
File
.
join
(
@path
,
params
[
:dir_name
])
@commit_params
=
{
file_path:
@dir_name
,
current_branch:
@ref
,
target_branch:
@new_branch
,
commit_message:
params
[
:commit_message
],
}
end
def
after_create_dir_path
if
create_merge_request?
new_merge_request_path
else
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
File
.
join
(
@new_branch
,
@dir_name
))
end
end
end
app/helpers/blob_helper.rb
View file @
adc77257
...
...
@@ -22,32 +22,92 @@ def no_highlight_files
%w(credits changelog news copying copyright license authors)
end
def
edit_blob_link
(
project
,
ref
,
path
,
options
=
{})
blob
=
begin
project
.
repository
.
blob_at
(
ref
,
path
)
rescue
nil
end
return
unless
blob
&&
blob
.
text?
&&
blob_editable?
(
blob
)
text
=
'Edit'
after
=
options
[
:after
]
||
''
def
edit_blob_link
(
project
=
@project
,
ref
=
@ref
,
path
=
@path
,
options
=
{})
return
unless
current_user
blob
=
project
.
repository
.
blob_at
(
ref
,
path
)
rescue
nil
return
unless
blob
&&
blob_text_editable?
(
blob
)
from_mr
=
options
[
:from_merge_request_id
]
link_opts
=
{}
link_opts
[
:from_merge_request_id
]
=
from_mr
if
from_mr
cls
=
'btn btn-small'
link_to
(
text
,
namespace_project_edit_blob_path
(
project
.
namespace
,
project
,
tree_join
(
ref
,
path
),
link_opts
),
class:
cls
)
+
after
.
html_safe
edit_path
=
namespace_project_edit_blob_path
(
project
.
namespace
,
project
,
tree_join
(
ref
,
path
),
link_opts
)
if
!
on_top_of_branch?
button_tag
"Edit"
,
class:
"btn btn-default disabled has_tooltip"
,
title:
"You can only edit files when you are on a branch"
,
data:
{
container:
'body'
}
elsif
can_edit_blob?
(
blob
)
link_to
"Edit"
,
edit_path
,
class:
'btn btn-small'
elsif
can?
(
current_user
,
:fork_project
,
project
)
continue_params
=
{
to:
edit_path
,
notice:
edit_in_new_fork_notice
,
notice_now:
edit_in_new_fork_notice_now
}
fork_path
=
namespace_project_fork_path
(
project
.
namespace
,
project
,
namespace_key:
current_user
.
namespace
.
id
,
continue:
continue_params
)
link_to
"Edit"
,
fork_path
,
class:
'btn btn-small'
,
method: :post
end
end
def
modify_file_link
(
project
=
@project
,
ref
=
@ref
,
path
=
@path
,
label
:,
action
:,
btn_class
:,
modal_type
:)
return
unless
current_user
blob
=
project
.
repository
.
blob_at
(
ref
,
path
)
rescue
nil
return
unless
blob
if
!
on_top_of_branch?
button_tag
label
,
class:
"btn btn-
#{
btn_class
}
disabled has_tooltip"
,
title:
"You can only
#{
action
}
files when you are on a branch"
,
data:
{
container:
'body'
}
elsif
can_edit_blob?
(
blob
)
button_tag
label
,
class:
"btn btn-
#{
btn_class
}
"
,
'data-target'
=>
"#modal-
#{
modal_type
}
-blob"
,
'data-toggle'
=>
'modal'
elsif
can?
(
current_user
,
:fork_project
,
project
)
continue_params
=
{
to:
request
.
fullpath
,
notice:
edit_in_new_fork_notice
+
" Try to
#{
action
}
this file again."
,
notice_now:
edit_in_new_fork_notice_now
}
fork_path
=
namespace_project_fork_path
(
project
.
namespace
,
project
,
namespace_key:
current_user
.
namespace
.
id
,
continue:
continue_params
)
link_to
label
,
fork_path
,
class:
"btn btn-
#{
btn_class
}
"
,
method: :post
end
end
def
replace_blob_link
(
project
=
@project
,
ref
=
@ref
,
path
=
@path
)
modify_file_link
(
project
,
ref
,
path
,
label:
"Replace"
,
action:
"replace"
,
btn_class:
"default"
,
modal_type:
"upload"
)
end
def
delete_blob_link
(
project
=
@project
,
ref
=
@ref
,
path
=
@path
)
modify_file_link
(
project
,
ref
,
path
,
label:
"Delete"
,
action:
"delete"
,
btn_class:
"remove"
,
modal_type:
"remove"
)
end
def
blob_text_editable?
(
blob
)
blob
.
text?
&&
!
blob
.
lfs_pointer?
end
def
blob
_edit
a
bl
e
?
(
blob
,
project
=
@project
,
ref
=
@ref
)
!
blob
.
lfs_pointer?
&&
allowed_tree_edit
?
(
project
,
ref
)
def
can
_edit
_
bl
ob
?
(
blob
,
project
=
@project
,
ref
=
@ref
)
!
blob
.
lfs_pointer?
&&
can_edit_tree
?
(
project
,
ref
)
end
def
leave_edit_message
...
...
app/helpers/tree_helper.rb
View file @
adc77257
...
...
@@ -50,24 +50,49 @@ def on_top_of_branch?(project = @project, ref = @ref)
project
.
repository
.
branch_names
.
include?
(
ref
)
end
def
allowed_tree_edit
?
(
project
=
nil
,
ref
=
nil
)
def
can_edit_tree
?
(
project
=
nil
,
ref
=
nil
)
project
||=
@project
ref
||=
@ref
return
false
unless
on_top_of_branch?
(
project
,
ref
)
can?
(
current_user
,
:push_code
,
project
)
can?
(
current_user
,
:push_code
,
project
)
||
(
current_user
&&
current_user
.
already_forked?
(
project
))
end
def
tree_edit_branch
(
project
=
@project
,
ref
=
@ref
)
if
allowed_tree_edit?
(
project
,
ref
)
if
can_push_branch?
(
project
,
ref
)
ref
else
project
.
repository
.
next_patch_branch
end
return
unless
can_edit_tree?
(
project
,
ref
)
if
can_push_branch?
(
project
,
ref
)
ref
else
project
=
tree_edit_project
(
project
)
project
.
repository
.
next_patch_branch
end
end
def
tree_edit_project
(
project
=
@project
)
if
can?
(
current_user
,
:push_code
,
project
)
project
elsif
current_user
&&
current_user
.
already_forked?
(
project
)
current_user
.
fork_of
(
project
)