Bilder-Download: Mehrfach-Auswahl + Ordner-ZIP
- Selection-Mode: Bilder in Galerie per Klick auswaehlen - 1-3 Bilder: einzeln herunterladen, 4+: als ZIP - Ordner-Download: ganzen zugewiesenen Ordner als ZIP - Sicherheit: Zugriffspruefung + Path-Traversal-Schutz - archiver Package fuer Streaming-ZIP-Erstellung Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -194,6 +194,7 @@
|
||||
|
||||
initThumbs();
|
||||
initPoptrox();
|
||||
initSelectionMode(images);
|
||||
},
|
||||
error: function() {
|
||||
$main.html('<p style="text-align:center;color:#fff;padding:2em;">Keine Bilder gefunden. Lade Bilder in deinen Nextcloud-Ordner hoch.</p>');
|
||||
@@ -290,6 +291,177 @@
|
||||
return template;
|
||||
}
|
||||
|
||||
// ==================== SELECTION MODE ====================
|
||||
|
||||
function initSelectionMode(allImages) {
|
||||
var selectedImages = [];
|
||||
var selectionActive = false;
|
||||
|
||||
// Toolbar erstellen (versteckt)
|
||||
var $toolbar = $('<div id="selection-toolbar"></div>');
|
||||
$toolbar.html(
|
||||
'<span id="sel-count">0 ausgewaehlt</span>' +
|
||||
'<button id="sel-download" class="button small" disabled>' +
|
||||
'<i class="fa fa-download"></i> Herunterladen</button>' +
|
||||
'<button id="sel-cancel" class="button small">' +
|
||||
'<i class="fa fa-times"></i> Abbrechen</button>'
|
||||
);
|
||||
$('body').append($toolbar);
|
||||
|
||||
// Selection-Toggle Button im Header
|
||||
var $selectToggle = $('<li><a href="#" id="select-mode-toggle" title="Bilder auswaehlen">' +
|
||||
'<i class="fa fa-check-square"></i></a></li>');
|
||||
$('#header nav > ul').prepend($selectToggle);
|
||||
|
||||
// Ordner-Download Buttons (nur wenn Ordner-Bilder vorhanden)
|
||||
var folders = [];
|
||||
allImages.forEach(function(img) {
|
||||
if (img.folder && folders.indexOf(img.folder) === -1) {
|
||||
folders.push(img.folder);
|
||||
}
|
||||
});
|
||||
if (folders.length > 0) {
|
||||
folders.forEach(function(folder) {
|
||||
var $folderBtn = $('<li><a href="#" class="folder-download-btn" ' +
|
||||
'data-folder="' + folder + '" title="Ordner \'' + folder + '\' herunterladen">' +
|
||||
'<i class="fa fa-folder-open"></i></a></li>');
|
||||
$('#header nav > ul').prepend($folderBtn);
|
||||
});
|
||||
}
|
||||
|
||||
// Selection-Mode toggle
|
||||
$(document).on('click', '#select-mode-toggle', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
selectionActive = !selectionActive;
|
||||
$('body').toggleClass('selection-mode', selectionActive);
|
||||
if (!selectionActive) clearSelection();
|
||||
$toolbar.toggleClass('visible', selectionActive);
|
||||
});
|
||||
|
||||
// Klick auf Thumbnail im Selection-Mode
|
||||
$(document).on('click', '.thumb', function(e) {
|
||||
if (!selectionActive) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
var $thumb = $(this);
|
||||
var $img = $thumb.find('img');
|
||||
var imgName = $img.data('name');
|
||||
var imgHref = $thumb.find('a.image').attr('href');
|
||||
|
||||
// Ordner aus href extrahieren
|
||||
var folder = null;
|
||||
var hrefPath = imgHref.replace('/images/fulls/', '');
|
||||
var parts = hrefPath.split('/');
|
||||
if (parts.length > 1) {
|
||||
folder = decodeURIComponent(parts[0]);
|
||||
}
|
||||
|
||||
var idx = -1;
|
||||
for (var i = 0; i < selectedImages.length; i++) {
|
||||
if (selectedImages[i].name === imgName && selectedImages[i].folder === folder) {
|
||||
idx = i; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx !== -1) {
|
||||
selectedImages.splice(idx, 1);
|
||||
$thumb.removeClass('selected');
|
||||
} else {
|
||||
selectedImages.push({ name: imgName, folder: folder });
|
||||
$thumb.addClass('selected');
|
||||
}
|
||||
updateToolbar();
|
||||
});
|
||||
|
||||
function updateToolbar() {
|
||||
var count = selectedImages.length;
|
||||
$('#sel-count').text(count + ' ausgewaehlt');
|
||||
$('#sel-download').prop('disabled', count === 0);
|
||||
if (count > 3) {
|
||||
$('#sel-download').html('<i class="fa fa-file-archive-o"></i> Als ZIP (' + count + ')');
|
||||
} else if (count > 0) {
|
||||
$('#sel-download').html('<i class="fa fa-download"></i> Herunterladen (' + count + ')');
|
||||
} else {
|
||||
$('#sel-download').html('<i class="fa fa-download"></i> Herunterladen');
|
||||
}
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
selectedImages = [];
|
||||
$('.thumb').removeClass('selected');
|
||||
updateToolbar();
|
||||
}
|
||||
|
||||
// Download-Button
|
||||
$(document).on('click', '#sel-download', function() {
|
||||
if (selectedImages.length === 0) return;
|
||||
$.ajax({
|
||||
url: '/api/download/selected',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({ images: selectedImages }),
|
||||
xhrFields: { responseType: 'blob' },
|
||||
success: function(data, status, xhr) {
|
||||
var ct = xhr.getResponseHeader('Content-Type');
|
||||
if (ct && ct.indexOf('application/json') !== -1) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
var resp = JSON.parse(this.result);
|
||||
if (resp.mode === 'individual') {
|
||||
resp.urls.forEach(function(url, i) {
|
||||
setTimeout(function() {
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = '';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
}, i * 300);
|
||||
});
|
||||
}
|
||||
};
|
||||
reader.readAsText(data);
|
||||
} else {
|
||||
var url = window.URL.createObjectURL(data);
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'mischkomposition-auswahl.zip';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
alert(xhr.responseJSON ? xhr.responseJSON.error : 'Download fehlgeschlagen');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Abbrechen-Button
|
||||
$(document).on('click', '#sel-cancel', function() {
|
||||
selectionActive = false;
|
||||
$('body').removeClass('selection-mode');
|
||||
$toolbar.removeClass('visible');
|
||||
clearSelection();
|
||||
});
|
||||
|
||||
// Ordner-Download
|
||||
$(document).on('click', '.folder-download-btn', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var folder = $(this).data('folder');
|
||||
var a = document.createElement('a');
|
||||
a.href = '/api/download/folder/' + encodeURIComponent(folder);
|
||||
a.download = folder + '.zip';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
});
|
||||
}
|
||||
|
||||
// Galerie laden
|
||||
loadGallery();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user