Add "Replace" and "Upload" features
Refactor upload and replace functionality
Rename file and move CSS
Fix typo
Make dropzone a div
Remove unnecessary file
Change color of "upload existing one"
Add missing changes
This commit is contained in:
parent
7abb744eb9
commit
e2ece2bc35
|
|
@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
|
|||
|
||||
v 8.0.0 (unreleased)
|
||||
- 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 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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
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 @@
|
|||
}
|
||||
|
||||
#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
|
|||
|
||||
if result[:status] == :success
|
||||
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
|
||||
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
|
||||
|
||||
|
|
@ -45,10 +51,16 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
|
||||
if result[:status] == :success
|
||||
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
|
||||
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
|
||||
|
||||
|
|
@ -146,11 +158,19 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
|
||||
@file_path =
|
||||
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]))
|
||||
else
|
||||
@path
|
||||
end
|
||||
|
||||
if params[:file].present?
|
||||
params[:content] = Base64.encode64(params[:file].read)
|
||||
params[:encoding] = 'base64'
|
||||
end
|
||||
|
||||
@commit_params = {
|
||||
file_path: @file_path,
|
||||
current_branch: @current_branch,
|
||||
|
|
|
|||
|
|
@ -19,10 +19,12 @@ module Files
|
|||
end
|
||||
|
||||
unless project.empty_repo?
|
||||
@file_path.slice!(0) if @file_path.start_with?('/')
|
||||
|
||||
blob = repository.blob_at_branch(@current_branch, @file_path)
|
||||
|
||||
if blob
|
||||
raise_error("Your changes could not be committed, because file with such name exists")
|
||||
raise_error("Your changes could not be committed because a file with the same name already exists")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@
|
|||
tree_join(@commit.sha, @path)), class: 'btn btn-sm'
|
||||
|
||||
- if allowed_tree_edit?
|
||||
= button_tag class: 'remove-blob btn btn-sm btn-remove',
|
||||
'data-toggle' => 'modal', 'data-target' => '#modal-remove-blob' do
|
||||
Remove
|
||||
.btn-group{:role => "group"}
|
||||
%button.btn.btn-default{class: 'btn-primary', href: '#modal-replace-blob', 'data-target' => '#modal-replace-blob', 'data-toggle' => 'modal'} Replace
|
||||
%button.btn.btn-default{class: 'btn-remove', href: '#modal-remove-blob', 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal'} Remove
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
#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
|
||||
%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')
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#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
|
||||
%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')
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
- page_title "New File", @ref
|
||||
%h3.page-title New file
|
||||
%h3.page-title<
|
||||
Create new file or
|
||||
%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
|
||||
= 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
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@
|
|||
|
||||
- if allowed_tree_edit?
|
||||
= render 'projects/blob/remove'
|
||||
= render 'projects/blob/replace'
|
||||
|
|
|
|||
|
|
@ -356,6 +356,16 @@ Gitlab::Application.routes.draw do
|
|||
to: 'blob#destroy',
|
||||
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
|
||||
|
||||
scope do
|
||||
|
|
|
|||
|
|
@ -34,6 +34,29 @@ Feature: Project Source Browse Files
|
|||
Then I am redirected to the new file
|
||||
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
|
||||
Scenario: I can create and commit file and specify new branch
|
||||
Given I click on "new file" link in repo
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# coding: utf-8
|
||||
class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
|
||||
include SharedAuthentication
|
||||
include SharedProject
|
||||
|
|
@ -78,7 +79,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
step 'I click link "Diff"' do
|
||||
|
|
@ -97,6 +98,14 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
|
|||
click_button 'Remove file'
|
||||
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
|
||||
expect(page).to have_css '.line_holder.new'
|
||||
end
|
||||
|
|
@ -106,10 +115,55 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
|
|||
end
|
||||
|
||||
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"
|
||||
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
|
||||
click_link 'files'
|
||||
end
|
||||
|
|
@ -232,4 +286,29 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
|
|||
def new_file_name
|
||||
'not_a_file.md'
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue