Merge pull request #1344 from gollum/ajax_file_upload
Improvements and fixes for file uploading. Prepares for handling of duplicate error.
This commit is contained in:
+1
-1
@@ -127,7 +127,7 @@ MSG
|
|||||||
end
|
end
|
||||||
opts.on("--allow-uploads [MODE]", [:dir, :page], "Enable file uploads.",
|
opts.on("--allow-uploads [MODE]", [:dir, :page], "Enable file uploads.",
|
||||||
"If set to 'dir', Gollum will store all uploads in the '<git-repo>/uploads/' directory.",
|
"If set to 'dir', Gollum will store all uploads in the '<git-repo>/uploads/' directory.",
|
||||||
"If set to 'page', Gollum will store each upload at the currently edited page.") do |mode|
|
"If set to 'page', Gollum will store uploads per page in '<git-repo>/uploads/[pagename]'.") do |mode|
|
||||||
wiki_options[:allow_uploads] = true
|
wiki_options[:allow_uploads] = true
|
||||||
wiki_options[:per_page_uploads] = true if mode == :page
|
wiki_options[:per_page_uploads] = true if mode == :page
|
||||||
end
|
end
|
||||||
|
|||||||
+20
-19
@@ -84,6 +84,7 @@ module Precious
|
|||||||
before do
|
before do
|
||||||
settings.wiki_options[:allow_editing] = settings.wiki_options.fetch(:allow_editing, true)
|
settings.wiki_options[:allow_editing] = settings.wiki_options.fetch(:allow_editing, true)
|
||||||
@allow_editing = settings.wiki_options[:allow_editing]
|
@allow_editing = settings.wiki_options[:allow_editing]
|
||||||
|
@per_page_uploads = settings.wiki_options[:per_page_uploads]
|
||||||
forbid unless @allow_editing || request.request_method == "GET"
|
forbid unless @allow_editing || request.request_method == "GET"
|
||||||
Precious::App.set(:mustache, {:templates => settings.wiki_options[:template_dir]}) if settings.wiki_options[:template_dir]
|
Precious::App.set(:mustache, {:templates => settings.wiki_options[:template_dir]}) if settings.wiki_options[:template_dir]
|
||||||
@base_url = url('/', false).chomp('/').force_encoding('utf-8')
|
@base_url = url('/', false).chomp('/').force_encoding('utf-8')
|
||||||
@@ -166,7 +167,6 @@ module Precious
|
|||||||
wikip = wiki_page(params[:splat].first)
|
wikip = wiki_page(params[:splat].first)
|
||||||
@name = join_page_name(wikip.name, wikip.ext)
|
@name = join_page_name(wikip.name, wikip.ext)
|
||||||
@path = wikip.path
|
@path = wikip.path
|
||||||
@upload_dest = find_upload_dest(@path)
|
|
||||||
|
|
||||||
wiki = wikip.wiki
|
wiki = wikip.wiki
|
||||||
@allow_uploads = wiki.allow_uploads
|
@allow_uploads = wiki.allow_uploads
|
||||||
@@ -180,14 +180,11 @@ module Precious
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# AJAX calls only
|
||||||
post '/upload_file' do
|
post '/upload_file' do
|
||||||
|
|
||||||
wiki = wiki_new
|
wiki = wiki_new
|
||||||
|
halt 405 unless wiki.allow_uploads
|
||||||
unless wiki.allow_uploads
|
|
||||||
@message = "File uploads are disabled"
|
|
||||||
mustache :error
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if params[:file]
|
if params[:file]
|
||||||
fullname = params[:file][:filename]
|
fullname = params[:file][:filename]
|
||||||
@@ -195,8 +192,21 @@ module Precious
|
|||||||
end
|
end
|
||||||
halt 500 unless tempfile.is_a? Tempfile
|
halt 500 unless tempfile.is_a? Tempfile
|
||||||
|
|
||||||
# Remove page file dir prefix from upload path if necessary -- committer handles this itself
|
if wiki.per_page_uploads
|
||||||
dir = wiki.per_page_uploads ? params[:upload_dest] : ::File.join([wiki.page_file_dir, 'uploads'].compact)
|
# remove base_url and gollum/* subpath if necessary
|
||||||
|
dir = request.referer.
|
||||||
|
sub(request.base_url, '').
|
||||||
|
sub(/.*gollum\/[-\w]+\//, '')
|
||||||
|
# remove file extension
|
||||||
|
dir = dir.sub(::File.extname(dir), '')
|
||||||
|
dir = ::File.join("uploads", dir)
|
||||||
|
else
|
||||||
|
# Remove page file dir prefix from upload path if necessary -- committer handles this itself
|
||||||
|
dir = ::File.join([wiki.page_file_dir, 'uploads'].compact)
|
||||||
|
end
|
||||||
|
halt 500 if dir.include?('..')
|
||||||
|
halt 500 unless Pathname(dir).relative?
|
||||||
|
|
||||||
ext = ::File.extname(fullname)
|
ext = ::File.extname(fullname)
|
||||||
format = ext.split('.').last || 'txt'
|
format = ext.split('.').last || 'txt'
|
||||||
filename = ::File.basename(fullname, ext)
|
filename = ::File.basename(fullname, ext)
|
||||||
@@ -224,8 +234,7 @@ module Precious
|
|||||||
committer.commit
|
committer.commit
|
||||||
redirect to(request.referer)
|
redirect to(request.referer)
|
||||||
rescue Gollum::DuplicatePageError => e
|
rescue Gollum::DuplicatePageError => e
|
||||||
@message = "Duplicate page: #{e.message}"
|
halt 409 # Signal conflict
|
||||||
mustache :error
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -308,7 +317,6 @@ module Precious
|
|||||||
@ext = wikip.ext
|
@ext = wikip.ext
|
||||||
@path = wikip.path
|
@path = wikip.path
|
||||||
@allow_uploads = wikip.wiki.allow_uploads
|
@allow_uploads = wikip.wiki.allow_uploads
|
||||||
@upload_dest = find_upload_dest(@path)
|
|
||||||
|
|
||||||
page_dir = settings.wiki_options[:page_file_dir].to_s
|
page_dir = settings.wiki_options[:page_file_dir].to_s
|
||||||
unless page_dir.empty?
|
unless page_dir.empty?
|
||||||
@@ -500,7 +508,6 @@ module Precious
|
|||||||
@page = page
|
@page = page
|
||||||
@name = name
|
@name = name
|
||||||
@content = page.formatted_data
|
@content = page.formatted_data
|
||||||
@upload_dest = find_upload_dest(path)
|
|
||||||
|
|
||||||
# Extensions and layout data
|
# Extensions and layout data
|
||||||
@editable = true
|
@editable = true
|
||||||
@@ -577,11 +584,5 @@ module Precious
|
|||||||
commit_message
|
commit_message
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_upload_dest(path)
|
|
||||||
settings.wiki_options[:allow_uploads] ?
|
|
||||||
(settings.wiki_options[:per_page_uploads] ?
|
|
||||||
"#{path}/#{@name}".sub(/^\/\//, '') : 'uploads'
|
|
||||||
) : ''
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -205,9 +205,8 @@
|
|||||||
|
|
||||||
var file = e.dataTransfer.files[0],
|
var file = e.dataTransfer.files[0],
|
||||||
formData = new FormData();
|
formData = new FormData();
|
||||||
formData.append('upload_dest', uploadDest);
|
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '<%= upload_file_path %>',
|
url: '<%= upload_file_path %>',
|
||||||
data: formData,
|
data: formData,
|
||||||
@@ -228,8 +227,8 @@
|
|||||||
}
|
}
|
||||||
window.ace_editor.insert(text);
|
window.ace_editor.insert(text);
|
||||||
},
|
},
|
||||||
error: function(r, textStatus) {
|
error: function(r, textStatus, errorThrown) {
|
||||||
alert('Error uploading file: ' + textStatus);
|
alert('Error uploading file: ' + textStatus + ' ' + errorThrown);
|
||||||
$editorBody.removeClass('uploading');
|
$editorBody.removeClass('uploading');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -105,8 +105,6 @@
|
|||||||
|
|
||||||
html += '<form method=post enctype="multipart/form-data" ' +
|
html += '<form method=post enctype="multipart/form-data" ' +
|
||||||
'action="' + action + '" ' + 'id="' + id + '">';
|
'action="' + action + '" ' + 'id="' + id + '">';
|
||||||
html += '<input type="hidden" name="upload_dest" value="' +
|
|
||||||
uploadDest + '">';
|
|
||||||
html += '<input type=file name="' + name + '">';
|
html += '<input type=file name="' + name + '">';
|
||||||
html += '</form>';
|
html += '</form>';
|
||||||
|
|
||||||
@@ -125,9 +123,9 @@
|
|||||||
title +'</h4></div>' +
|
title +'</h4></div>' +
|
||||||
'<div id="gollum-dialog-dialog-body">' + body + '</div>' +
|
'<div id="gollum-dialog-dialog-body">' + body + '</div>' +
|
||||||
'<div id="gollum-dialog-dialog-buttons">' +
|
'<div id="gollum-dialog-dialog-buttons">' +
|
||||||
'<a href="#" title="Cancel" id="gollum-dialog-action-cancel" ' +
|
'<button name="Cancel" id="gollum-dialog-action-cancel" ' +
|
||||||
'class="gollum-minibutton">Cancel</a>' +
|
'class="gollum-minibutton">Cancel</a>' +
|
||||||
'<a href="#" title="OK" id="gollum-dialog-action-ok" '+
|
'<button name="OK" id="gollum-dialog-action-ok" '+
|
||||||
'class="gollum-minibutton">OK</a>' +
|
'class="gollum-minibutton">OK</a>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
@@ -139,9 +137,9 @@
|
|||||||
title +'</h4></div>' +
|
title +'</h4></div>' +
|
||||||
'<div id="gollum-dialog-dialog-body">' + body + '</div>' +
|
'<div id="gollum-dialog-dialog-body">' + body + '</div>' +
|
||||||
'<div id="gollum-dialog-dialog-buttons">' +
|
'<div id="gollum-dialog-dialog-buttons">' +
|
||||||
'<a href="#" title="Cancel" id="gollum-dialog-action-cancel" ' +
|
'<button name="Cancel" id="gollum-dialog-action-cancel" ' +
|
||||||
'class="minibutton">Cancel</a>' +
|
'class="minibutton">Cancel</button>' +
|
||||||
'<a href="#" title="OK" id="gollum-dialog-action-ok" '+
|
'<button name="OK" id="gollum-dialog-action-ok" '+
|
||||||
'class="minibutton">OK</a>' +
|
'class="minibutton">OK</a>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// ua detection
|
// ua detection
|
||||||
if ($.browser.mozilla) {
|
if ($.browser.mozilla) {
|
||||||
$('body').addClass('ff');
|
$('body').addClass('ff');
|
||||||
@@ -176,7 +177,7 @@ $(document).ready(function() {
|
|||||||
$('#minibutton-upload-page').parent().removeClass('jaws');
|
$('#minibutton-upload-page').parent().removeClass('jaws');
|
||||||
$('#minibutton-upload-page').click(function(e) {
|
$('#minibutton-upload-page').click(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
$.GollumDialog.init({
|
$.GollumDialog.init({
|
||||||
title: 'Upload File',
|
title: 'Upload File',
|
||||||
fields: [
|
fields: [
|
||||||
@@ -187,9 +188,40 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
OK: function( res ) {
|
OK: function( res ) {
|
||||||
$('#upload').submit();
|
$('#wiki-content').addClass('uploading');
|
||||||
|
var formData = new FormData($('#upload').get(0));
|
||||||
|
var endpoint = $('#upload').attr("action");
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: endpoint,
|
||||||
|
type: 'POST',
|
||||||
|
data: formData,
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
success: function(data) {
|
||||||
|
// File successfully uploaded
|
||||||
|
$('#wiki-content').removeClass('uploading');
|
||||||
|
},
|
||||||
|
error: function(data, textStatus, errorThrown) {
|
||||||
|
$('#wiki-content').removeClass('uploading');
|
||||||
|
if (data.status == 409) {
|
||||||
|
alert('This file already exists.');
|
||||||
|
} else {
|
||||||
|
alert('Error uploading file: ' + textStatus + ' ' + errorThrown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$('#gollum-dialog-action-ok').attr('disabled', true);
|
||||||
|
$('input:file').on('change', function() {
|
||||||
|
if ($(this).val()) {
|
||||||
|
news = 'Your uploaded file will be accessible at<br>/' + uploadDest + '/' + $('input[type=file]').val().split('\\').pop();
|
||||||
|
$(".context").html(news);
|
||||||
|
$('#gollum-dialog-action-ok').attr('disabled', false);
|
||||||
|
|
||||||
|
};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,12 @@
|
|||||||
height: 1%;
|
height: 1%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.uploading {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @section body */
|
/* @section body */
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.minibutton {
|
button.minibutton {
|
||||||
float: right;
|
float: right;
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
width: auto;
|
width: auto;
|
||||||
@@ -42,6 +42,12 @@
|
|||||||
|
|
||||||
@include button-base;
|
@include button-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.minibutton:disabled {
|
||||||
|
opacity: 0.65;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#gollum-dialog-dialog-inner {
|
#gollum-dialog-dialog-inner {
|
||||||
|
|||||||
@@ -15,11 +15,15 @@
|
|||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var baseUrl = '{{base_url}}';
|
var baseUrl = '{{base_url}}';
|
||||||
var uploadDest = '{{upload_dest}}';
|
var uploadDest = 'uploads';
|
||||||
{{#page}}
|
var perPageUploads = '{{per_page_uploads}}';
|
||||||
var pageFullPath = '{{url_path}}';
|
if (perPageUploads == 'true') {
|
||||||
{{/page}}
|
uploadDest = uploadDest + window.location.pathname.replace(/.*gollum\/[-\w]+\//, "/").replace(/\.[^/.]+$/, "")
|
||||||
|
}
|
||||||
|
{{#page}}
|
||||||
|
var pageFullPath = '{{url_path}}';
|
||||||
|
{{/page}}
|
||||||
</script>
|
</script>
|
||||||
{{#sprockets_javascript_tag}}app{{/sprockets_javascript_tag}}
|
{{#sprockets_javascript_tag}}app{{/sprockets_javascript_tag}}
|
||||||
{{#use_identicon}}
|
{{#use_identicon}}
|
||||||
|
|||||||
@@ -21,10 +21,6 @@ module Precious
|
|||||||
@allow_uploads
|
@allow_uploads
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload_dest
|
|
||||||
@upload_dest
|
|
||||||
end
|
|
||||||
|
|
||||||
def format
|
def format
|
||||||
@format ||= find_format.to_s.downcase
|
@format ||= find_format.to_s.downcase
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -64,10 +64,6 @@ module Precious
|
|||||||
@allow_uploads
|
@allow_uploads
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload_dest
|
|
||||||
@upload_dest
|
|
||||||
end
|
|
||||||
|
|
||||||
def format
|
def format
|
||||||
@format = (@page.format || false) if @format.nil?
|
@format = (@page.format || false) if @format.nil?
|
||||||
@format.to_s.downcase
|
@format.to_s.downcase
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ module Precious
|
|||||||
def js # custom js
|
def js # custom js
|
||||||
@js
|
@js
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def per_page_uploads
|
||||||
|
@per_page_uploads
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -57,10 +57,6 @@ module Precious
|
|||||||
@allow_uploads
|
@allow_uploads
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload_dest
|
|
||||||
@upload_dest
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_header
|
def has_header
|
||||||
if @header
|
if @header
|
||||||
@header.formatted_data.strip.empty? ? false : true
|
@header.formatted_data.strip.empty? ? false : true
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ context "Precious::Views::Editing" do
|
|||||||
|
|
||||||
assert_match /Delete this Page/, last_response.body, "'Delete this Page' link is blocked in page template"
|
assert_match /Delete this Page/, last_response.body, "'Delete this Page' link is blocked in page template"
|
||||||
assert_match /New/, last_response.body, "'New' button is blocked in page template"
|
assert_match /New/, last_response.body, "'New' button is blocked in page template"
|
||||||
assert_match /Upload/, last_response.body, "'Upload' link is blocked in page template"
|
assert_match /Upload\b/, last_response.body, "'Upload' link is blocked in page template"
|
||||||
assert_match /Rename/, last_response.body, "'Rename' link is blocked in page template"
|
assert_match /Rename/, last_response.body, "'Rename' link is blocked in page template"
|
||||||
assert_match /Edit/, last_response.body, "'Edit' link is blocked in page template"
|
assert_match /Edit/, last_response.body, "'Edit' link is blocked in page template"
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ context "Precious::Views::Editing" do
|
|||||||
|
|
||||||
assert_no_match /Delete this Page/, last_response.body, "'Delete this Page' link not blocked in page template"
|
assert_no_match /Delete this Page/, last_response.body, "'Delete this Page' link not blocked in page template"
|
||||||
assert_no_match /New/, last_response.body, "'New' button not blocked in page template"
|
assert_no_match /New/, last_response.body, "'New' button not blocked in page template"
|
||||||
assert_no_match /Upload/, last_response.body, "'Upload' link not blocked in page template"
|
assert_no_match /Upload\b/, last_response.body, "'Upload' link not blocked in page template"
|
||||||
assert_no_match /Rename/, last_response.body, "'Rename' link not blocked in page template"
|
assert_no_match /Rename/, last_response.body, "'Rename' link not blocked in page template"
|
||||||
assert_no_match /Edit/, last_response.body, "'Edit' link not blocked in page template"
|
assert_no_match /Edit/, last_response.body, "'Edit' link not blocked in page template"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user