Commit 81e741af authored by Stan Hu's avatar Stan Hu

Merge pull request #9138 from liyakun/master

add "replace" and "upload" functionality
parents c99473e5 e2ece2bc
...@@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.0.0 (unreleased) v 8.0.0 (unreleased)
- Bump rouge to 1.10.1 to remove warning noise and fix other syntax highlighting bugs (Stan Hu) - Bump rouge to 1.10.1 to remove warning noise and fix other syntax highlighting bugs (Stan Hu)
- Gracefully handle errors in syntax highlighting by leaving the block unformatted (Stan Hu) - Gracefully handle errors in syntax highlighting by leaving the block unformatted (Stan Hu)
- Add "replace" and "upload" functionalities to allow user replace existing file and upload new file into current repository
- Fix URL construction for merge requests, issues, notes, and commits for relative URL config (Stan Hu) - Fix URL construction for merge requests, issues, notes, and commits for relative URL config (Stan Hu)
- Fix emoji URLs in Markdown when relative_url_root is used (Stan Hu) - Fix emoji URLs in Markdown when relative_url_root is used (Stan Hu)
- Omit filename in Content-Disposition header in raw file download to avoid RFC 6266 encoding issues (Stan HU) - Omit filename in Content-Disposition header in raw file download to avoid RFC 6266 encoding issues (Stan HU)
......
class @BlobFileDropzone
constructor: (form, method) ->
form_dropzone = form.find('.dropzone')
Dropzone.autoDiscover = false
dropzone = form_dropzone.dropzone(
autoDiscover: false
autoProcessQueue: false
url: form.attr('action')
# Rails uses a hidden input field for PUT
# http://stackoverflow.com/questions/21056482/how-to-set-method-put-in-form-tag-in-rails
method: method
clickable: true
uploadMultiple: false
paramName: "file"
maxFilesize: gon.max_file_size or 10
parallelUploads: 1
maxFiles: 1
addRemoveLinks: true
previewsContainer: '.dropzone-previews'
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
success: (header, response) ->
window.location.href = response.filePath
return
error: (temp, errorMessage) ->
stripped = $("<div/>").html(errorMessage).text();
$('.dropzone-alerts').html('Error uploading file: \"' + stripped + '\"').show()
return
maxfilesexceeded: (file) ->
@removeFile file
return
removedfile: (file) ->
$('.dropzone-previews')[0].removeChild(file.previewTemplate)
$('.dropzone-alerts').html('').hide()
return true
sending: (file, xhr, formData) ->
formData.append('commit_message', form.find('#commit_message').val())
return
)
submitButton = form.find('#submit-all')[0]
submitButton.addEventListener 'click', (e) ->
e.preventDefault()
e.stopPropagation()
alert "Please select a file" if dropzone[0].dropzone.getQueuedFiles().length == 0
dropzone[0].dropzone.processQueue()
return false
...@@ -116,3 +116,15 @@ ...@@ -116,3 +116,15 @@
} }
#modal-remove-blob > .modal-dialog { width: 850px; } #modal-remove-blob > .modal-dialog { width: 850px; }
.blob-upload-dropzone-previews {
text-align: center;
border: 2px;
border-style: dashed;
min-height: 200px;
}
.upload-link {
font-weight: normal;
color: #0000EE;
}
...@@ -26,10 +26,16 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -26,10 +26,16 @@ class Projects::BlobController < Projects::ApplicationController
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) respond_to do |format|
format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) }
format.json { render json: { message: "success", filePath: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } }
end
else else
flash[:alert] = result[:message] flash[:alert] = result[:message]
render :new respond_to do |format|
format.html { render :new }
format.json { render json: { message: "failed", filePath: namespace_project_new_blob_path(@project.namespace, @project, @id) } }
end
end end
end end
...@@ -45,10 +51,16 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -45,10 +51,16 @@ class Projects::BlobController < Projects::ApplicationController
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
redirect_to after_edit_path respond_to do |format|
format.html { redirect_to after_edit_path }
format.json { render json: { message: "success", filePath: after_edit_path } }
end
else else
flash[:alert] = result[:message] flash[:alert] = result[:message]
render :edit respond_to do |format|
format.html { render :edit }
format.json { render json: { message: "failed", filePath: namespace_project_new_blob_path(@project.namespace, @project, @id) } }
end
end end
end end
...@@ -146,11 +158,19 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -146,11 +158,19 @@ class Projects::BlobController < Projects::ApplicationController
@file_path = @file_path =
if action_name.to_s == 'create' if action_name.to_s == 'create'
if params[:file].present?
params[:file_name] = params[:file].original_filename
end
File.join(@path, File.basename(params[:file_name])) File.join(@path, File.basename(params[:file_name]))
else else
@path @path
end end
if params[:file].present?
params[:content] = Base64.encode64(params[:file].read)
params[:encoding] = 'base64'
end
@commit_params = { @commit_params = {
file_path: @file_path, file_path: @file_path,
current_branch: @current_branch, current_branch: @current_branch,
......
...@@ -19,6 +19,8 @@ module Files ...@@ -19,6 +19,8 @@ module Files
end end
unless project.empty_repo? unless project.empty_repo?
@file_path.slice!(0) if @file_path.start_with?('/')
blob = repository.blob_at_branch(@current_branch, @file_path) blob = repository.blob_at_branch(@current_branch, @file_path)
if blob if blob
......
...@@ -17,6 +17,6 @@ ...@@ -17,6 +17,6 @@
tree_join(@commit.sha, @path)), class: 'btn btn-sm' tree_join(@commit.sha, @path)), class: 'btn btn-sm'
- if allowed_tree_edit? - if allowed_tree_edit?
= button_tag class: 'remove-blob btn btn-sm btn-remove', .btn-group{:role => "group"}
'data-toggle' => 'modal', 'data-target' => '#modal-remove-blob' do %button.btn.btn-default{class: 'btn-primary', href: '#modal-replace-blob', 'data-target' => '#modal-replace-blob', 'data-toggle' => 'modal'} Replace
Remove %button.btn.btn-default{class: 'btn-remove', href: '#modal-remove-blob', 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal'} Remove
#modal-replace-blob.modal
.modal-dialog
.modal-content
.modal-header
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title Replace #{@blob.name}
%p.light
From branch
%strong= @ref
.modal-body
= form_tag namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'blob-file-upload-form-js form-horizontal' do
.dropzone
.dropzone-previews{class: "blob-upload-dropzone-previews"}
%p.dz-message{class: "hint"}<
Attach files by dragging & dropping or&nbsp;
%a{href: '#', class: "markdown-selector"}>click to upload
%br
.dropzone-alerts{class: "alert alert-danger data", "data-dismiss" => "alert", style: "display:none"}
= render 'shared/commit_message_container', params: params,
placeholder: 'Replace this file because...'
.form-group
.col-sm-offset-2.col-sm-10
= button_tag 'Replace file', class: 'btn btn-small btn-primary btn-replace-file', id: 'submit-all'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
:coffeescript
disableButtonIfEmptyField $('.blob-file-upload-form-js').find('#commit_message'), '.btn-replace-file'
new BlobFileDropzone($('.blob-file-upload-form-js'), 'put')
#modal-upload-blob.modal
.modal-dialog
.modal-content
.modal-header
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title Upload
%p.light
From branch
%strong= @ref
.modal-body
= form_tag namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'blob-file-upload-form-js form-horizontal' do
.dropzone
.dropzone-previews{class: "blob-upload-dropzone-previews"}
%p.dz-message{class: "hint"}<
Attach files by dragging & dropping or&nbsp;
%a{href: '#', class: "markdown-selector"}>click to upload
%br
.dropzone-alerts{class: "alert alert-danger data", "data-dismiss" => "alert", style: "display:none"}
= render 'shared/commit_message_container', params: params,
placeholder: 'Upload this file because...'
.form-group
.col-sm-offset-2.col-sm-10
= button_tag 'Upload file', class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
:coffeescript
disableButtonIfEmptyField $('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file'
new BlobFileDropzone($('.blob-file-upload-form-js'), 'post')
- page_title "New File", @ref %h3.page-title<
%h3.page-title New file Create new file or&nbsp;
%a.upload-link{href: '#modal-upload-blob', 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'}>upload existing one
.file-title
= render 'projects/blob/upload'
%br
.file-editor .file-editor
= form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file js-requires-input') do = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file js-requires-input') do
= render 'projects/blob/editor', ref: @ref = render 'projects/blob/editor', ref: @ref
......
...@@ -10,3 +10,4 @@ ...@@ -10,3 +10,4 @@
- if allowed_tree_edit? - if allowed_tree_edit?
= render 'projects/blob/remove' = render 'projects/blob/remove'
= render 'projects/blob/replace'
...@@ -358,6 +358,16 @@ Gitlab::Application.routes.draw do ...@@ -358,6 +358,16 @@ Gitlab::Application.routes.draw do
to: 'blob#destroy', to: 'blob#destroy',
constraints: { id: /.+/, format: false } constraints: { id: /.+/, format: false }
) )
put(
'/blob/*id',
to: 'blob#update',
constraints: { id: /.+/, format: false }
)
post(
'/blob/*id',
to: 'blob#create',
constraints: { id: /.+/, format: false }
)
end end
scope do scope do
......
...@@ -33,6 +33,29 @@ Feature: Project Source Browse Files ...@@ -33,6 +33,29 @@ Feature: Project Source Browse Files
And I click on "Commit Changes" And I click on "Commit Changes"
Then I am redirected to the new file Then I am redirected to the new file
And I should see its new content And I should see its new content
@javascript
Scenario: I can upload file and commit
Given I click on "new file" link in repo
Then I can see new file page
And I can see "upload existing one"
And I click on "upload existing one"
And I upload a new text file
And I fill the upload file commit message
And I click on "Upload file"
Then I can see the new text file
And I can see the new commit message
@javascript
Scenario: I can replace file and commit
Given I click on ".gitignore" file in repo
And I see the ".gitignore"
And I click on "Replace"
And I replace it with a text file
And I fill the replace file commit message
And I click on "Replace file"
Then I can see the new text file
And I can see the replacement commit message
@javascript @javascript
Scenario: I can create and commit file and specify new branch Scenario: I can create and commit file and specify new branch
......
# coding: utf-8
class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedProject include SharedProject
...@@ -78,7 +79,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -78,7 +79,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end end
step 'I fill the commit message' do step 'I fill the commit message' do
fill_in :commit_message, with: 'Not yet a commit message.' fill_in :commit_message, with: 'Not yet a commit message.', visible: true
end end
step 'I click link "Diff"' do step 'I click link "Diff"' do
...@@ -97,6 +98,14 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -97,6 +98,14 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
click_button 'Remove file' click_button 'Remove file'
end end
step 'I click on "Replace"' do
click_button "Replace"
end
step 'I click on "Replace file"' do
click_button 'Replace file'
end
step 'I see diff' do step 'I see diff' do
expect(page).to have_css '.line_holder.new' expect(page).to have_css '.line_holder.new'
end end
...@@ -106,10 +115,55 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -106,10 +115,55 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end end
step 'I can see new file page' do step 'I can see new file page' do
expect(page).to have_content "New file" expect(page).to have_content "new file"
expect(page).to have_content "Commit message" expect(page).to have_content "Commit message"
end end
step 'I can see "upload existing one"' do
expect(page).to have_content "upload existing one"
end
step 'I click on "upload existing one"' do
click_link 'upload existing one'
end
step 'I click on "Upload file"' do
click_button 'Upload file'
end
step 'I can see the new commit message' do
expect(page).to have_content "New upload commit message"
end
step 'I upload a new text file' do
drop_in_dropzone test_text_file
end
step 'I fill the upload file commit message' do
page.within('#modal-upload-blob') do
fill_in :commit_message, with: 'New upload commit message'
end
end
step 'I replace it with a text file' do
drop_in_dropzone test_text_file
end
step 'I fill the replace file commit message' do
page.within('#modal-replace-blob') do
fill_in :commit_message, with: 'Replacement file commit message'
end
end
step 'I can see the replacement commit message' do
expect(page).to have_content "Replacement file commit message"
end
step 'I can see the new text file' do
expect(page).to have_content "Lorem ipsum dolor sit amet"
expect(page).to have_content "Sed ut perspiciatis unde omnis"
end
step 'I click on files directory' do step 'I click on files directory' do
click_link 'files' click_link 'files'
end end
...@@ -232,4 +286,29 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -232,4 +286,29 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
def new_file_name def new_file_name
'not_a_file.md' 'not_a_file.md'
end end
def drop_in_dropzone(file_path)
# Generate a fake input selector
page.execute_script <<-JS
var fakeFileInput = window.$('<input/>').attr(
{id: 'fakeFileInput', type: 'file'}
).appendTo('body');
JS
# Attach the file to the fake input selector with Capybara
attach_file("fakeFileInput", file_path)
# Add the file to a fileList array and trigger the fake drop event
page.execute_script <<-JS
var fileList = [$('#fakeFileInput')[0].files[0]];
var e = jQuery.Event('drop', { dataTransfer : { files : fileList } });
$('.dropzone')[0].dropzone.listeners[0].events.drop(e);
JS
end
def test_text_file
File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')
end
def test_image_file
File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')
end
end end
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