got drag n drop to work with firefox -and- chrome and checking in before I manage to break it again

This commit is contained in:
Kyle Drake 2024-03-10 16:22:12 -05:00
parent a0707e9d54
commit a91041ae61
11 changed files with 148 additions and 171 deletions

View file

@ -74,7 +74,6 @@ def extract_files(params, files = [])
files
end
post '/api/upload' do
require_api_credentials
files = extract_files params

View file

@ -84,7 +84,7 @@ class Site < Sequel::Model
SCREENSHOT_RESOLUTIONS = ['540x405', '210x158', '100x100', '50x50']
THUMBNAIL_RESOLUTIONS = ['210x158']
MAX_FILE_SIZE = 10**8 # 100 MB
MAX_FILE_SIZE = 10**8 # 100 MB, change dashboard.js dropzone file size limit if you change this
CLAMAV_THREAT_MATCHES = [
/^VBS/,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

58
public/img/drag-drop.svg Normal file
View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="67.754738mm"
height="3.4416525mm"
viewBox="0 0 67.754738 3.4416525"
version="1.1"
id="svg5"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
sodipodi:docname="drag-drop.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="3.1108586"
inkscape:cx="71.523663"
inkscape:cy="-55.772384"
inkscape:window-width="2490"
inkscape:window-height="1376"
inkscape:window-x="70"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-23.11286,-40.759304)">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52778px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="22.954386"
y="43.43959"
id="text82970"><tspan
sodipodi:role="line"
id="tspan82968"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.52778px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#f6f0e6;fill-opacity:1;stroke-width:0.264583"
x="22.954386"
y="43.43959">drag and drop files here to upload</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -123,8 +123,6 @@ function hideUploadProgress() {
$('#uploadingOverlay').css('display', 'none')
}
allUploadsComplete = false
$('#createDir').on('shown', function () {
$('#newDirInput').focus();
})
@ -147,167 +145,6 @@ function iconView() {
$('#filesDisplay').removeClass('list-view')
}
// Drop handler function to get all files
async function getAllFileEntries(dataTransferItemList) {
let fileEntries = [];
// Use BFS to traverse entire directory/file structure
let queue = [];
for (let i = 0; i < dataTransferItemList.length; i++) {
queue.push(dataTransferItemList[i].webkitGetAsEntry());
}
while (queue.length > 0) {
let entry = queue.shift();
if (entry.isFile) {
fileEntries.push(entry);
} else if (entry.isDirectory) {
let reader = entry.createReader();
queue.push(...await readAllDirectoryEntries(reader));
}
}
return fileEntries;
}
// Get all the entries (files or sub-directories) in a directory
async function readAllDirectoryEntries(directoryReader) {
let entries = [];
let readEntries = await readEntriesPromise(directoryReader);
while (readEntries.length > 0) {
entries.push(...readEntries);
readEntries = await readEntriesPromise(directoryReader);
}
return entries;
}
// Wrap readEntries in a promise
async function readEntriesPromise(directoryReader) {
try {
return await new Promise((resolve, reject) => {
directoryReader.readEntries(resolve, reject);
});
} catch (err) {
console.log(err);
}
}
async function uploadFile(file, dir, additionalFormData) {
const formData = new FormData();
// Append additional form data (from other input fields) to each file's FormData
for (const [key, value] of Object.entries(additionalFormData)) {
formData.append(key, value);
}
var modifiedFileName;
if (file.webkitRelativePath === '') {
modifiedFileName = file.name;
} else {
modifiedFileName = file.webkitRelativePath;
}
if (dir && dir !== '/') {
modifiedFileName = dir.replace(/^\//, '') + '/' + modifiedFileName;
}
modifiedFileName = modifiedFileName.replace(/^\//, '');
console.log('modifiedFileName: '+modifiedFileName)
formData.append(modifiedFileName, file, modifiedFileName);
$('#uploadFileName').text(modifiedFileName).prepend('<i class="icon-file"></i> ');
// Send the FormData with the file and additional data
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const result = await response.json();
if (result.result == 'error') {
fileUploadErrorCount++;
if(fileUploadErrorCount == 1) {
alertType('error');
}
alertAdd(result.message);
} else {
fileUploadSuccessCount++;
}
} catch (err) {
}
}
async function processEntry(entry, dir, additionalFormData) {
await new Promise((resolve) => {
entry.file((file) => {
uploadFile(file, dir, additionalFormData).then(resolve);
});
});
}
async function uploadFiles(fileEntries) {
alertClear();
// Collect additional form data
const form = document.getElementById('dropzone');
let additionalFormData = {};
for (let i = 0; i < form.elements.length; i++) {
const input = form.elements[i];
if (input.name && input.type !== "file") { // Avoid file inputs
additionalFormData[input.name] = input.value;
}
}
const dir = additionalFormData['dir'] || '';
var totalFiles = fileEntries.length;
$('#progressBar').css('display', 'block')
fileUploadCount = 0
fileUploadErrorCount = 0
fileUploadSuccessCount = 0
for (let entry of fileEntries) {
await processEntry(entry, dir, additionalFormData);
fileUploadCount++;
var progress = (fileUploadCount / totalFiles) * 100;
$('#uploadingProgress').css('width', progress+'%');
}
allUploadsComplete = true
if(fileUploadErrorCount > 0) {
alertAdd(fileUploadSuccessCount+'/'+fileUploadCount+' files uploaded successfully.');
} else {
alertType('success')
alertAdd(fileUploadSuccessCount+' files uploaded successfully.');
}
reloadDashboardFiles();
}
function reInitDashboardFiles() {
var elDrop = document.getElementById('dropzone');
elDrop.addEventListener('dragover', function (event) {
event.preventDefault();
});
elDrop.addEventListener('drop', async function (event) {
event.preventDefault();
showUploadProgress();
let items = await getAllFileEntries(event.dataTransfer.items);
await uploadFiles(items);
});
}
function reloadDashboardFiles() {
$.get('/dashboard/files?dir='+encodeURIComponent($("#dir").val()), function(data) {
$('#filesDisplay').html(data);
reInitDashboardFiles();
});
}
function alertAdd(text) {
var a = $('#alertDialogue');
a.css('display', 'block');
@ -327,5 +164,86 @@ function alertType(type){
a.addClass('alert-'+type);
}
var processedFiles = 0;
var uploadedFiles = 0;
var uploadedFileErrors = 0;
function joinPaths(...paths) {
return paths
.map(path => path.replace(/(^\/|\/$)/g, ''))
.filter(path => path !== '')
.join('/');
}
function reInitDashboardFiles() {
new Dropzone("#uploads", {
url: "/api/upload",
paramName: 'file',
dictDefaultMessage: "",
uploadMultiple: false,
parallelUploads: 1,
maxFilesize: 104857600, // 100MB
clickable: false,
init: function() {
this.on("processing", function(file) {
var dir = $('#uploads input[name="dir"]').val();
if(file.fullPath) {
this.options.paramName = joinPaths(dir,file.fullPath);
} else {
this.options.paramName = joinPaths(dir, file.name);
}
processedFiles++;
$('#uploadFileName').text(this.options.paramName).prepend('<i class="icon-file"></i> ');
});
this.on("success", function(file) {
uploadedFiles++;
});
this.on("error", function(file, message) {
uploadedFiles++;
uploadedFileErrors++;
alertType('error');
if (message && message.message) {
alertAdd(message.message);
} else {
alertAdd(this.options.paramName+' failed to upload');
}
});
this.on("queuecomplete", function() {
hideUploadProgress();
if(uploadedFileErrors > 0) {
alertType('error');
alertAdd(uploadedFiles-uploadedFileErrors+'/'+uploadedFiles+' files uploaded successfully');
} else {
alertType('success');
alertAdd(uploadedFiles+' files uploaded successfully');
}
reloadDashboardFiles();
});
this.on("addedfiles", function(files) {
uploadedFiles = 0;
uploadedFileErrors = 0;
alertClear();
showUploadProgress();
});
}
});
}
function reloadDashboardFiles() {
$.get('/dashboard/files?dir='+encodeURIComponent($("#dir").val()), function(data) {
$('#filesDisplay').html(data);
reInitDashboardFiles();
});
}
// for first time load
reInitDashboardFiles();

2
public/js/dropzone-min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -352,7 +352,7 @@
min-height: 300px;
}
.files .list .upload-Boundary.with-instruction {
background: url(/img/drag-drop.png) no-repeat center center;
background: url(/img/drag-drop.svg) no-repeat center center;
@media (max-device-width:480px), screen and (max-width:800px) {
background: 0;

View file

@ -40,11 +40,11 @@
</div>
</div>
<div class="list">
<form action="/site_files/upload" id="dropzone">
<form action="/site_files/upload" class="dropzone" id="uploads">
<div class="dz-message" style="display: none"></div>
<input name="csrf_token" type="hidden" value="<%= csrf_token %>">
<input name="dir" type="hidden" value="<%= @dir %>">
<div class="upload-Boundary <%= @file_list.length <= 5 ? 'with-instruction' : '' %>">
<div class="upload-Boundary with-instruction">
<% @file_list.each do |file| %>
<div class="file filehover">
<% if file[:is_html] && current_site.screenshot_exists?(file[:path], '210x158') %>

View file

@ -1,3 +1,4 @@
<script src="/js/dropzone-min.js"></script>
<style>
.dz-default {
display: none;
@ -15,7 +16,6 @@
display: none;
}
</style>
<div class="header-Outro with-site-image dashboard">
<div class="row content wide">
<div class="col col-50 signup-Area">