drag/drop feature for new content #443

This commit is contained in:
Karel Picman 2016-08-25 14:23:04 +02:00
parent 7626815961
commit cfbae13b56
18 changed files with 304 additions and 46 deletions

View File

@ -135,7 +135,8 @@ class DmsfFilesController < ApplicationController
else
revision.increase_version(version)
end
file_upload = params[:file_upload]
#file_upload = params[:file_upload]
file_upload = params[:attachments]['1'] if params[:attachments].present?
unless file_upload
revision.size = last_revision.size
revision.disk_filename = last_revision.disk_filename
@ -146,10 +147,11 @@ class DmsfFilesController < ApplicationController
revision.digest = last_revision.digest
end
else
upload = DmsfUpload.create_from_uploaded_attachment(@project, @folder, file_upload)
revision.size = file_upload.size
revision.disk_filename = revision.new_storage_filename
revision.mime_type = Redmine::MimeType.of(file_upload.original_filename)
revision.digest = DmsfFileRevision.create_digest file_upload.path
revision.mime_type = upload.mime_type
revision.digest = DmsfFileRevision.create_digest upload.disk_file
end
# Custom fields
@ -163,8 +165,8 @@ class DmsfFilesController < ApplicationController
if revision.save
revision.assign_workflow(params[:dmsf_workflow_id])
if file_upload
revision.copy_file_content(file_upload)
if upload
FileUtils.mv(upload.disk_file, revision.disk_file)
end
if @file.locked? && !@file.locks.empty?
begin

View File

@ -201,7 +201,7 @@ class DmsfFile < ActiveRecord::Base
def notify?
return true if self.notification
return true if self.dmsf_folder && delf.dmsf_folder.notify?
return true if self.dmsf_folder && self.dmsf_folder.notify?
return true if !self.dmsf_folder && self.project.dmsf_notification
return false
end

View File

@ -46,6 +46,13 @@
<%= f.text_area(:description, :rows => 6, :class => 'wiki-edit') %>
</p>
<div class="clear">
<div class="splitcontentright">
<div class="custom_fields">
<% @revision.custom_field_values.each do |value| %>
<p><%= custom_field_tag_with_label(:dmsf_file_revision, value) %></p>
<% end %>
</div>
</div>
<div class="splitcontentleft">
<p>
<%= label_tag('version_0', l(:label_dmsf_version)) %>
@ -62,34 +69,15 @@
<%= select_tag 'custom_version_major', options_for_select(0..99, @file.last_revision.major_version + 2), :onchange => '$("#version_3").prop("checked", true)' %>.
<%= select_tag 'custom_version_minor', options_for_select(0..99, @file.last_revision.minor_version + 1), :onchange => '$("#version_3").prop("checked", true)' %>
<%= l(:option_version_custom) %>
</p>
</div>
<div class="splitcontentright">
<p>
<%= label_tag('file_upload', l(:label_new_content)) %>
<span class="add_attachment">
<%= file_field_tag 'file_upload',
:id => 'file_upload',
:class => 'file_selector',
:multiple => false,
:onchange => "$('#dmsf_file_revision_name').val(this.files[0].name)",
:data => {
:max_file_size => Setting.attachment_max_size.to_i.kilobytes,
:max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)),
:max_concurrent_uploads => 1,
:upload_path => uploads_path(:format => 'js')
}
%>
(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
</span>
</p>
</div>
</div>
<div class="custom_fields">
<% @revision.custom_field_values.each do |value| %>
<p><%= custom_field_tag_with_label(:dmsf_file_revision, value) %></p>
<% end %>
</div>
</div>
<div class="box">
<p>
<%= label_tag('file_upload', l(:label_new_content)) %>
<%= render :partial => 'upload_form' %>
</p>
</div>
<p>
<%= f.text_area(:comment, :rows => 2, :label => l(:label_comment)) %>
</p>
@ -99,4 +87,4 @@
</div>
</div>
<%= wikitoolbar_for 'dmsf_file_revision_description' %>
<%= wikitoolbar_for 'dmsf_file_revision_description' %>

View File

@ -0,0 +1,36 @@
<% hide = false %>
<span id="attachments_fields">
<% if defined?(container) && container && container.saved_attachments %>
<% container.saved_attachments.each_with_index do |attachment, i| %>
<span id="attachments_p<%= i %>">
<%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename') %>
<%= hidden_field_tag "attachments[p#{i}][token]", "#{attachment.token}" %>
<% hide = true %>
</span>
<% end %>
<% end %>
</span>
<% unless hide %>
<span class="add_attachment">
<%= file_field_tag 'attachments[dummy][file]',
:id => nil,
:class => 'file_selector',
:multiple => false,
:onchange => 'addInputFile(this);',
:data => {
:max_file_size => Setting.attachment_max_size.to_i.kilobytes,
:max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)),
:max_file_count_message => l(:error_maximum_upload_filecount, :filecount => 1),
:max_concurrent_uploads => Redmine::Configuration['max_concurrent_ajax_uploads'].to_i,
:upload_path => uploads_path(:format => 'js'),
:description_placeholder => l(:label_optional_description)
} %>
(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
</span>
<% end %>
<% content_for :header_tags do %>
<%= javascript_include_tag 'attachments_dmsf', :plugin => 'redmine_dmsf' %>
<% end %>

View File

@ -0,0 +1,206 @@
/* Redmine - project management software
Copyright (C) 2006-2016 Jean-Philippe Lang */
function addFile(inputEl, file, eagerUpload) {
if ($('#attachments_fields').children().length < 10) {
var attachmentId = addFile.nextAttachmentId++;
var fileSpan = $('<span>', { id: 'attachments_' + attachmentId });
fileSpan.append(
$('<input>', { type: 'text', 'class': 'filename readonly', name: 'attachments[' + attachmentId + '][filename]', readonly: 'readonly'} ).val(file.name)
).appendTo('#attachments_fields');
if(eagerUpload) {
ajaxUpload(file, attachmentId, fileSpan, inputEl);
}
toggleFileAdding(false);
$('#dmsf_file_revision_name').val(file.name);
return attachmentId;
}
return null;
}
addFile.nextAttachmentId = 1;
function ajaxUpload(file, attachmentId, fileSpan, inputEl) {
function onLoadstart(e) {
fileSpan.removeClass('ajax-waiting');
fileSpan.addClass('ajax-loading');
$('input:submit', $(this).parents('form')).attr('disabled', 'disabled');
}
function onProgress(e) {
if(e.lengthComputable) {
this.progressbar( 'value', e.loaded * 100 / e.total );
}
}
function actualUpload(file, attachmentId, fileSpan, inputEl) {
ajaxUpload.uploading++;
uploadBlob(file, $(inputEl).data('upload-path'), attachmentId, {
loadstartEventHandler: onLoadstart.bind(progressSpan),
progressEventHandler: onProgress.bind(progressSpan)
})
.done(function(result) {
progressSpan.progressbar( 'value', 100 ).remove();
fileSpan.find('input.description, a').css('display', 'inline-block');
})
.fail(function(result) {
progressSpan.text(result.statusText);
}).always(function() {
ajaxUpload.uploading--;
fileSpan.removeClass('ajax-loading');
var form = fileSpan.parents('form');
if (form.queue('upload').length == 0 && ajaxUpload.uploading == 0) {
$('input:submit', form).removeAttr('disabled');
}
form.dequeue('upload');
});
}
var progressSpan = $('<div>').insertAfter(fileSpan.find('input.filename'));
progressSpan.progressbar();
fileSpan.addClass('ajax-waiting');
var maxSyncUpload = $(inputEl).data('max-concurrent-uploads');
if(maxSyncUpload == null || maxSyncUpload <= 0 || ajaxUpload.uploading < maxSyncUpload)
actualUpload(file, attachmentId, fileSpan, inputEl);
else
$(inputEl).parents('form').queue('upload', actualUpload.bind(this, file, attachmentId, fileSpan, inputEl));
}
ajaxUpload.uploading = 0;
function removeFile() {
$(this).parent('span').remove();
toggleFileAdding(true);
return false;
}
function uploadBlob(blob, uploadUrl, attachmentId, options) {
var actualOptions = $.extend({
loadstartEventHandler: $.noop,
progressEventHandler: $.noop
}, options);
uploadUrl = uploadUrl + '?attachment_id=' + attachmentId;
if (blob instanceof window.File) {
uploadUrl += '&filename=' + encodeURIComponent(blob.name);
uploadUrl += '&content_type=' + encodeURIComponent(blob.type);
}
return $.ajax(uploadUrl, {
type: 'POST',
contentType: 'application/octet-stream',
beforeSend: function(jqXhr, settings) {
jqXhr.setRequestHeader('Accept', 'application/js');
// attach proper File object
settings.data = blob;
},
xhr: function() {
var xhr = $.ajaxSettings.xhr();
xhr.upload.onloadstart = actualOptions.loadstartEventHandler;
xhr.upload.onprogress = actualOptions.progressEventHandler;
return xhr;
},
data: blob,
cache: false,
processData: false
});
}
function addInputFile(inputEl) {
var clearedFileInput = $(inputEl).clone().val('');
if ($.ajaxSettings.xhr().upload && inputEl.files) {
// upload files using ajax
uploadAndAttachFiles(inputEl.files, inputEl);
$(inputEl).remove();
} else {
// browser not supporting the file API, upload on form submission
var attachmentId;
var aFilename = inputEl.value.split(/\/|\\/);
attachmentId = addFile(inputEl, { name: aFilename[ aFilename.length - 1 ] }, false);
if (attachmentId) {
$(inputEl).attr({ name: 'attachments[' + attachmentId + '][file]', style: 'display:none;' }).appendTo('#attachments_' + attachmentId);
}
}
clearedFileInput.insertAfter('#attachments_fields');
toggleFileAdding(false);
}
function uploadAndAttachFiles(files, inputEl) {
var maxFileSize = $(inputEl).data('max-file-size');
var maxFileSizeExceeded = $(inputEl).data('max-file-size-message');
var maxFileCountExceeded = $(inputEl).data('max-file-count-message');
var sizeExceeded = false;
$.each(files, function() {
if (this.size && maxFileSize != null && this.size > parseInt(maxFileSize)) {sizeExceeded=true;}
});
if((files.length > 1) || (!$('input.file_selector').is(':visible'))){
window.alert(maxFileCountExceeded);
}
else if (sizeExceeded) {
window.alert(maxFileSizeExceeded);
} else {
$.each(files, function() {addFile(inputEl, this, true);});
}
}
function toggleFileAdding(toggle){
$('input.file_selector').toggle(toggle);
$('span.add_attachment').toggle(toggle);
}
function handleFileDropEvent(e) {
$(this).removeClass('fileover');
blockEventPropagation(e);
if ($.inArray('Files', e.dataTransfer.types) > -1) {
uploadAndAttachFiles(e.dataTransfer.files, $('input:file.file_selector'));
}
}
function dragOverHandler(e) {
$(this).addClass('fileover');
blockEventPropagation(e);
}
function dragOutHandler(e) {
$(this).removeClass('fileover');
blockEventPropagation(e);
}
function setupFileDrop() {
if (window.File && window.FileList && window.ProgressEvent && window.FormData) {
$.event.fixHooks.drop = { props: [ 'dataTransfer' ] };
$('form div.box').has('input:file').each(function() {
$(this).on({
dragover: dragOverHandler,
dragleave: dragOutHandler,
drop: handleFileDropEvent
});
});
}
}
$(document).ready(setupFileDrop);

View File

@ -330,4 +330,6 @@ cs:
label_maximum_ajax_upload_filesize: Maximální velikost souboru nahratelná přes AJAX
note_maximum_ajax_upload_filesize: Omezuje velikost souboru, který může být nahrán přes standardní rozhraní AJAX, jinak se použije standardní rozhraní Redminu. Číslo je v MB.
label_classic: Klasický
label_drag_drop: "Drag&Drop"
label_drag_drop: Moderní
error_maximum_upload_filecount: "Nelze nahrát více než %{filecount} soubor(ů)."

View File

@ -330,4 +330,6 @@ de:
label_maximum_ajax_upload_filesize: Maximale Dateigröße für den Upload via AJAX
note_maximum_ajax_upload_filesize: Maximale Dateigröße für den Upload über die AJAX-Schnittstelle. Für größere Dateien muss der Standard-Uploader von Redmine verwendet werden. Angabe in MB.
label_classic: Klassisch
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "Nicht mehr als %{filecount} Datai(en) kann man hochladen."

View File

@ -330,4 +330,6 @@ en:
label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX
note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB.
label_classic: Classic
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "No more than %{filecount} file(s) can be uploaded."

View File

@ -330,4 +330,6 @@ es:
label_maximum_ajax_upload_filesize: "El máximo tamaño de archivo para subir por AJAX"
note_maximum_ajax_upload_filesize: "El límite máximo de tamaño de archivo que puede ser subido por la interfaz AJAX estandar, de lo contrario se debe utilizar el formulario estandar de Redmine. El número es en MB."
label_classic: Classic
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "No more than %{filecount} file(s) can be uploaded."

View File

@ -330,4 +330,6 @@ fr:
label_maximum_ajax_upload_filesize: Taille maximale de fichier pour téléversement via AJAX
note_maximum_ajax_upload_filesize: "Taille maximale, en méga octets, de fichier pour téléversement via l'interface standard AJAX. Sinon l'interface standard de Redmine doit être utilisée."
label_classic: Classic
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "No more than %{filecount} file(s) can be uploaded."

View File

@ -330,4 +330,6 @@ it: # Italian strings thx 2 Matteo Arceci!
label_maximum_ajax_upload_filesize: Dimensione massima dei documenti caricabili tramite AJAX
note_maximum_ajax_upload_filesize: Limita la dimensione massima dei documenti che possono essere caricati tramite interfaccia standard AJAX altrimenti sarà necessario utilizzare il modulo standard di Redmine. Il numero è espresso in MB.
label_classic: Classic
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "No more than %{filecount} file(s) can be uploaded."

View File

@ -330,4 +330,6 @@ ja:
label_maximum_ajax_upload_filesize: アップロードファイルサイズ上限
note_maximum_ajax_upload_filesize: アップロード可能なファイルサイズの上限。AjaxおよびRedmineの仕様に制限される2ギガバイト程度までは確認済み単位はMB。
label_classic: Classic
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "No more than %{filecount} file(s) can be uploaded."

View File

@ -330,4 +330,6 @@ pl:
label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX
note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB.
label_classic: Classic
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "No more than %{filecount} file(s) can be uploaded."

View File

@ -330,4 +330,6 @@ pt-BR:
label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX
note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB.
label_classic: Classic
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "No more than %{filecount} file(s) can be uploaded."

View File

@ -330,4 +330,6 @@ ru:
label_maximum_ajax_upload_filesize: Максимальный размер файла, загружаемого посредством AJAX
note_maximum_ajax_upload_filesize: Превышает максимальный размер файла, загружаемого посредством интерфейса AJAX, для загрузки можно использовать стандартную форму Redmine. Размер указан в MB.
label_classic: Classic
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "No more than %{filecount} file(s) can be uploaded."

View File

@ -330,4 +330,6 @@ sl:
label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX
note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB.
label_classic: Classic
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "No more than %{filecount} file(s) can be uploaded."

View File

@ -330,4 +330,6 @@ zh-TW:
label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX
note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB.
label_classic: Classic
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "No more than %{filecount} file(s) can be uploaded."

View File

@ -330,4 +330,6 @@ zh:
label_maximum_ajax_upload_filesize: Maximum file size uploadable via AJAX
note_maximum_ajax_upload_filesize: Limits maximum file size that can uploaded via standard AJAX interface otherwise Redmine standard upload form must be used. Number is in MB.
label_classic: Classic
label_drag_drop: "Drag&Drop"
label_drag_drop: Modern
error_maximum_upload_filecount: "No more than %{filecount} file(s) can be uploaded."