952 lines
33 KiB
JavaScript
952 lines
33 KiB
JavaScript
// fileActions.js
|
||
import { showToast, attachEnterKeyListener } from './domUtils.js?v={{APP_QVER}}';
|
||
import { loadFileList } from './fileListView.js?v={{APP_QVER}}';
|
||
import { formatFolderName } from './fileListView.js?v={{APP_QVER}}';
|
||
import { refreshFolderIcon } from './folderManager.js?v={{APP_QVER}}';
|
||
import { t } from './i18n.js?v={{APP_QVER}}';
|
||
|
||
export function handleDeleteSelected(e) {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
const checkboxes = document.querySelectorAll(".file-checkbox:checked");
|
||
if (checkboxes.length === 0) {
|
||
showToast("no_files_selected");
|
||
return;
|
||
}
|
||
window.filesToDelete = Array.from(checkboxes).map(chk => chk.value);
|
||
const count = window.filesToDelete.length;
|
||
document.getElementById("deleteFilesMessage").textContent = t("confirm_delete_files", { count: count });
|
||
document.getElementById("deleteFilesModal").style.display = "block";
|
||
attachEnterKeyListener("deleteFilesModal", "confirmDeleteFiles");
|
||
}
|
||
|
||
const FILE_MODAL_IDS = [
|
||
'deleteFilesModal',
|
||
'downloadZipModal',
|
||
'downloadProgressModal',
|
||
'createFileModal',
|
||
'downloadFileModal',
|
||
'copyFilesModal',
|
||
'moveFilesModal',
|
||
'renameFileModal',
|
||
'createFolderModal', // if this exists in your HTML
|
||
];
|
||
|
||
function portalFileModalsToBody() {
|
||
FILE_MODAL_IDS.forEach(id => {
|
||
const el = document.getElementById(id);
|
||
if (el && el.parentNode !== document.body) {
|
||
document.body.appendChild(el);
|
||
}
|
||
});
|
||
}
|
||
|
||
|
||
// --- Upload modal "portal" support ---
|
||
let _uploadCardSentinel = null;
|
||
|
||
export function openUploadModal() {
|
||
const modal = document.getElementById('uploadModal');
|
||
const body = document.getElementById('uploadModalBody');
|
||
const card = document.getElementById('uploadCard'); // <-- your existing card
|
||
window.openUploadModal = openUploadModal;
|
||
window.__pendingDropData = null;
|
||
if (!modal || !body || !card) {
|
||
console.warn('Upload modal or upload card not found');
|
||
return;
|
||
}
|
||
|
||
// Create a hidden sentinel so we can put the card back in place later
|
||
if (!_uploadCardSentinel) {
|
||
_uploadCardSentinel = document.createElement('div');
|
||
_uploadCardSentinel.id = 'uploadCardSentinel';
|
||
_uploadCardSentinel.style.display = 'none';
|
||
card.parentNode.insertBefore(_uploadCardSentinel, card);
|
||
}
|
||
|
||
// Move the actual card node into the modal (keeps all existing listeners)
|
||
body.appendChild(card);
|
||
|
||
// Show modal
|
||
modal.style.display = 'block';
|
||
|
||
// Focus the chooser for quick keyboard flow
|
||
setTimeout(() => {
|
||
const chooseBtn = document.getElementById('customChooseBtn');
|
||
if (chooseBtn) chooseBtn.focus();
|
||
}, 50);
|
||
}
|
||
|
||
export function closeUploadModal() {
|
||
const modal = document.getElementById('uploadModal');
|
||
const card = document.getElementById('uploadCard');
|
||
|
||
if (_uploadCardSentinel && _uploadCardSentinel.parentNode && card) {
|
||
_uploadCardSentinel.parentNode.insertBefore(card, _uploadCardSentinel);
|
||
}
|
||
if (modal) modal.style.display = 'none';
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
const cancelDelete = document.getElementById("cancelDeleteFiles");
|
||
if (cancelDelete) {
|
||
cancelDelete.addEventListener("click", function () {
|
||
document.getElementById("deleteFilesModal").style.display = "none";
|
||
window.filesToDelete = [];
|
||
});
|
||
}
|
||
|
||
const confirmDelete = document.getElementById("confirmDeleteFiles");
|
||
if (confirmDelete) {
|
||
confirmDelete.setAttribute("data-default", "");
|
||
confirmDelete.addEventListener("click", function () {
|
||
fetch("/api/file/deleteFiles.php", {
|
||
method: "POST",
|
||
credentials: "include",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"X-CSRF-Token": window.csrfToken
|
||
},
|
||
body: JSON.stringify({ folder: window.currentFolder, files: window.filesToDelete })
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast("Selected files deleted successfully!");
|
||
loadFileList(window.currentFolder);
|
||
refreshFolderIcon(window.currentFolder);
|
||
} else {
|
||
showToast("Error: " + (data.error || "Could not delete files"));
|
||
}
|
||
})
|
||
.catch(error => console.error("Error deleting files:", error))
|
||
.finally(() => {
|
||
document.getElementById("deleteFilesModal").style.display = "none";
|
||
window.filesToDelete = [];
|
||
});
|
||
});
|
||
}
|
||
});
|
||
|
||
attachEnterKeyListener("downloadZipModal", "confirmDownloadZip");
|
||
export function handleDownloadZipSelected(e) {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
const checkboxes = document.querySelectorAll(".file-checkbox:checked");
|
||
if (checkboxes.length === 0) {
|
||
showToast("No files selected for download.");
|
||
return;
|
||
}
|
||
window.filesToDownload = Array.from(checkboxes).map(chk => chk.value);
|
||
document.getElementById("downloadZipModal").style.display = "block";
|
||
setTimeout(() => {
|
||
const input = document.getElementById("zipFileNameInput");
|
||
input.focus();
|
||
}, 100);
|
||
};
|
||
|
||
export function handleCreateFileSelected(e) {
|
||
e.preventDefault(); e.stopImmediatePropagation();
|
||
const modal = document.getElementById('createFileModal');
|
||
modal.style.display = 'block';
|
||
setTimeout(() => {
|
||
const inp = document.getElementById('newFileCreateName');
|
||
if (inp) inp.focus();
|
||
}, 100);
|
||
}
|
||
|
||
/**
|
||
* Open the “New File” modal
|
||
*/
|
||
export function openCreateFileModal() {
|
||
const modal = document.getElementById('createFileModal');
|
||
const input = document.getElementById('createFileNameInput');
|
||
if (!modal || !input) {
|
||
console.error('Create-file modal or input not found');
|
||
return;
|
||
}
|
||
input.value = '';
|
||
modal.style.display = 'block';
|
||
setTimeout(() => input.focus(), 0);
|
||
}
|
||
|
||
|
||
export async function handleCreateFile(e) {
|
||
e.preventDefault();
|
||
const input = document.getElementById('createFileNameInput');
|
||
if (!input) return console.error('Create-file input missing');
|
||
const name = input.value.trim();
|
||
if (!name) {
|
||
showToast(t('newfile_placeholder')); // or a more explicit error
|
||
return;
|
||
}
|
||
|
||
const folder = window.currentFolder || 'root';
|
||
try {
|
||
const res = await fetch('/api/file/createFile.php', {
|
||
method: 'POST',
|
||
credentials: 'include',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-CSRF-Token': window.csrfToken
|
||
},
|
||
// ⚠️ must send `name`, not `filename`
|
||
body: JSON.stringify({ folder, name })
|
||
});
|
||
const js = await res.json();
|
||
if (!js.success) throw new Error(js.error);
|
||
showToast(t('file_created'));
|
||
loadFileList(folder);
|
||
refreshFolderIcon(folder);
|
||
} catch (err) {
|
||
showToast(err.message || t('error_creating_file'));
|
||
} finally {
|
||
document.getElementById('createFileModal').style.display = 'none';
|
||
}
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const cancel = document.getElementById('cancelCreateFile');
|
||
const confirm = document.getElementById('confirmCreateFile');
|
||
if (cancel) cancel.addEventListener('click', () => document.getElementById('createFileModal').style.display = 'none');
|
||
if (confirm) confirm.addEventListener('click', handleCreateFile);
|
||
});
|
||
|
||
export function openDownloadModal(fileName, folder) {
|
||
// Store file details globally for the download confirmation function.
|
||
window.singleFileToDownload = fileName;
|
||
window.currentFolder = folder || "root";
|
||
|
||
// Optionally pre-fill the file name input in the modal.
|
||
const input = document.getElementById("downloadFileNameInput");
|
||
if (input) {
|
||
input.value = fileName; // Use file name as-is (or modify if desired)
|
||
}
|
||
|
||
// Show the single file download modal (a new modal element).
|
||
document.getElementById("downloadFileModal").style.display = "block";
|
||
|
||
// Optionally focus the input after a short delay.
|
||
setTimeout(() => {
|
||
if (input) input.focus();
|
||
}, 100);
|
||
}
|
||
|
||
export function confirmSingleDownload() {
|
||
// 1) Get and validate the filename
|
||
const input = document.getElementById("downloadFileNameInput");
|
||
const fileName = input.value.trim();
|
||
if (!fileName) {
|
||
showToast("Please enter a name for the file.");
|
||
return;
|
||
}
|
||
|
||
// 2) Hide the download-name modal
|
||
document.getElementById("downloadFileModal").style.display = "none";
|
||
|
||
// 3) Build the direct download URL
|
||
const folder = window.currentFolder || "root";
|
||
const downloadURL = "/api/file/download.php"
|
||
+ "?folder=" + encodeURIComponent(folder)
|
||
+ "&file=" + encodeURIComponent(window.singleFileToDownload);
|
||
|
||
// 4) Trigger native browser download
|
||
const a = document.createElement("a");
|
||
a.href = downloadURL;
|
||
a.download = fileName;
|
||
a.style.display = "none";
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
|
||
// 5) Notify the user
|
||
showToast("Download started. Check your browser’s download manager.");
|
||
}
|
||
|
||
export function handleExtractZipSelected(e) {
|
||
if (e) {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
}
|
||
const checkboxes = document.querySelectorAll(".file-checkbox:checked");
|
||
if (!checkboxes.length) {
|
||
showToast("No files selected.");
|
||
return;
|
||
}
|
||
const zipFiles = Array.from(checkboxes)
|
||
.map(chk => chk.value)
|
||
.filter(name => name.toLowerCase().endsWith(".zip"));
|
||
if (!zipFiles.length) {
|
||
showToast("No zip files selected.");
|
||
return;
|
||
}
|
||
|
||
// Prepare and show the spinner-only modal
|
||
const modal = document.getElementById("downloadProgressModal");
|
||
const titleEl = document.getElementById("downloadProgressTitle");
|
||
const spinner = modal.querySelector(".download-spinner");
|
||
const progressBar = document.getElementById("downloadProgressBar");
|
||
const progressPct = document.getElementById("downloadProgressPercent");
|
||
|
||
if (titleEl) titleEl.textContent = "Extracting files…";
|
||
if (spinner) spinner.style.display = "inline-block";
|
||
if (progressBar) progressBar.style.display = "none";
|
||
if (progressPct) progressPct.style.display = "none";
|
||
|
||
modal.style.display = "block";
|
||
|
||
fetch("/api/file/extractZip.php", {
|
||
method: "POST",
|
||
credentials: "include",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"X-CSRF-Token": window.csrfToken
|
||
},
|
||
body: JSON.stringify({
|
||
folder: window.currentFolder || "root",
|
||
files: zipFiles
|
||
})
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
modal.style.display = "none";
|
||
if (data.success) {
|
||
let msg = "Zip file(s) extracted successfully!";
|
||
if (Array.isArray(data.extractedFiles) && data.extractedFiles.length) {
|
||
msg = "Extracted: " + data.extractedFiles.join(", ");
|
||
}
|
||
showToast(msg);
|
||
loadFileList(window.currentFolder);
|
||
} else {
|
||
showToast("Error extracting zip: " + (data.error || "Unknown error"));
|
||
}
|
||
})
|
||
.catch(error => {
|
||
modal.style.display = "none";
|
||
console.error("Error extracting zip files:", error);
|
||
showToast("Error extracting zip files.");
|
||
});
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const zipNameModal = document.getElementById("downloadZipModal");
|
||
const progressModal = document.getElementById("downloadProgressModal");
|
||
const cancelZipBtn = document.getElementById("cancelDownloadZip");
|
||
const confirmZipBtn = document.getElementById("confirmDownloadZip");
|
||
const cancelCreate = document.getElementById('cancelCreateFile');
|
||
|
||
if (cancelCreate) {
|
||
cancelCreate.addEventListener('click', () => {
|
||
document.getElementById('createFileModal').style.display = 'none';
|
||
});
|
||
}
|
||
|
||
const confirmCreate = document.getElementById('confirmCreateFile');
|
||
if (confirmCreate) {
|
||
confirmCreate.addEventListener('click', async () => {
|
||
const name = document.getElementById('newFileCreateName').value.trim();
|
||
if (!name) {
|
||
showToast(t('please_enter_filename'));
|
||
return;
|
||
}
|
||
document.getElementById('createFileModal').style.display = 'none';
|
||
try {
|
||
const res = await fetch('/api/file/createFile.php', {
|
||
method: 'POST',
|
||
credentials: 'include',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-CSRF-Token': window.csrfToken
|
||
},
|
||
body: JSON.stringify({
|
||
folder: window.currentFolder || 'root',
|
||
filename: name
|
||
})
|
||
});
|
||
const js = await res.json();
|
||
if (!res.ok || !js.success) {
|
||
throw new Error(js.error || t('error_creating_file'));
|
||
}
|
||
showToast(t('file_created_successfully'));
|
||
loadFileList(window.currentFolder);
|
||
refreshFolderIcon(folder);
|
||
} catch (err) {
|
||
console.error(err);
|
||
showToast(err.message || t('error_creating_file'));
|
||
}
|
||
});
|
||
attachEnterKeyListener('createFileModal', 'confirmCreateFile');
|
||
}
|
||
|
||
// 1) Cancel button hides the name modal
|
||
if (cancelZipBtn) {
|
||
cancelZipBtn.addEventListener("click", () => {
|
||
zipNameModal.style.display = "none";
|
||
});
|
||
}
|
||
|
||
// 2) Confirm button kicks off the zip+download
|
||
if (confirmZipBtn) {
|
||
confirmZipBtn.setAttribute("data-default", "");
|
||
confirmZipBtn.addEventListener("click", async () => {
|
||
// a) Validate ZIP filename
|
||
let zipName = document.getElementById("zipFileNameInput").value.trim();
|
||
if (!zipName) { showToast("Please enter a name for the zip file."); return; }
|
||
if (!zipName.toLowerCase().endsWith(".zip")) zipName += ".zip";
|
||
|
||
// b) Hide the name‐input modal, show the progress modal
|
||
zipNameModal.style.display = "none";
|
||
progressModal.style.display = "block";
|
||
|
||
// c) Title text (optional)
|
||
const titleEl = document.getElementById("downloadProgressTitle");
|
||
if (titleEl) titleEl.textContent = `Preparing ${zipName}…`;
|
||
|
||
// d) Queue the job
|
||
const res = await fetch("/api/file/downloadZip.php", {
|
||
method: "POST",
|
||
credentials: "include",
|
||
headers: { "Content-Type": "application/json", "X-CSRF-Token": window.csrfToken },
|
||
body: JSON.stringify({ folder: window.currentFolder || "root", files: window.filesToDownload })
|
||
});
|
||
const jsr = await res.json().catch(() => ({}));
|
||
if (!res.ok || !jsr.ok) {
|
||
const msg = (jsr && jsr.error) ? jsr.error : `Status ${res.status}`;
|
||
throw new Error(msg);
|
||
}
|
||
const token = jsr.token;
|
||
const statusUrl = jsr.statusUrl;
|
||
const downloadUrl = jsr.downloadUrl + "&name=" + encodeURIComponent(zipName);
|
||
|
||
// Ensure a progress UI exists in the modal
|
||
function ensureZipProgressUI() {
|
||
const modalEl = document.getElementById("downloadProgressModal");
|
||
if (!modalEl) {
|
||
// really shouldn't happen, but fall back to body
|
||
console.warn("downloadProgressModal not found; falling back to document.body");
|
||
}
|
||
// Prefer a dedicated content node inside the modal
|
||
let host =
|
||
(modalEl && modalEl.querySelector("#downloadProgressContent")) ||
|
||
(modalEl && modalEl.querySelector(".modal-body")) ||
|
||
(modalEl && modalEl.querySelector(".rise-modal-body")) ||
|
||
(modalEl && modalEl.querySelector(".modal-content")) ||
|
||
(modalEl && modalEl.querySelector(".content")) ||
|
||
null;
|
||
|
||
// If no suitable container, create one inside the modal
|
||
if (!host) {
|
||
host = document.createElement("div");
|
||
host.id = "downloadProgressContent";
|
||
(modalEl || document.body).appendChild(host);
|
||
}
|
||
|
||
// Helper: ensure/move an element with given id into host
|
||
function ensureInHost(id, tag, init) {
|
||
let el = document.getElementById(id);
|
||
if (el && el.parentElement !== host) host.appendChild(el); // move if it exists elsewhere
|
||
if (!el) {
|
||
el = document.createElement(tag);
|
||
el.id = id;
|
||
if (typeof init === "function") init(el);
|
||
host.appendChild(el);
|
||
}
|
||
return el;
|
||
}
|
||
|
||
// Title
|
||
const title = ensureInHost("downloadProgressTitle", "div", (el) => {
|
||
el.style.marginBottom = "8px";
|
||
el.textContent = "Preparing…";
|
||
});
|
||
|
||
// Progress bar (native <progress>)
|
||
const bar = (function () {
|
||
let el = document.getElementById("downloadProgressBar");
|
||
if (el && el.parentElement !== host) host.appendChild(el); // move into modal
|
||
if (!el) {
|
||
el = document.createElement("progress");
|
||
el.id = "downloadProgressBar";
|
||
host.appendChild(el);
|
||
}
|
||
el.max = 100;
|
||
el.value = 0;
|
||
el.style.display = ""; // override any inline display:none
|
||
el.style.width = "100%";
|
||
el.style.height = "1.1em";
|
||
return el;
|
||
})();
|
||
|
||
// Text line
|
||
const text = ensureInHost("downloadProgressText", "div", (el) => {
|
||
el.style.marginTop = "8px";
|
||
el.style.fontSize = "0.9rem";
|
||
el.style.whiteSpace = "nowrap";
|
||
el.style.overflow = "hidden";
|
||
el.style.textOverflow = "ellipsis";
|
||
});
|
||
|
||
// Optional spinner hider
|
||
const hideSpinner = () => {
|
||
const sp = document.getElementById("downloadSpinner");
|
||
if (sp) sp.style.display = "none";
|
||
};
|
||
|
||
return { bar, text, title, hideSpinner };
|
||
}
|
||
|
||
function humanBytes(n) {
|
||
if (!Number.isFinite(n) || n < 0) return "";
|
||
const u = ["B", "KB", "MB", "GB", "TB"]; let i = 0, x = n;
|
||
while (x >= 1024 && i < u.length - 1) { x /= 1024; i++; }
|
||
return x.toFixed(x >= 10 || i === 0 ? 0 : 1) + " " + u[i];
|
||
}
|
||
function mmss(sec) {
|
||
sec = Math.max(0, sec | 0);
|
||
const m = (sec / 60) | 0, s = sec % 60;
|
||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||
}
|
||
|
||
const ui = ensureZipProgressUI();
|
||
const t0 = Date.now();
|
||
|
||
// e) Poll until ready
|
||
while (true) {
|
||
await new Promise(r => setTimeout(r, 1200));
|
||
const s = await fetch(`${statusUrl}&_=${Date.now()}`, {
|
||
credentials: "include", cache: "no-store",
|
||
}).then(r => r.json());
|
||
|
||
if (s.error) throw new Error(s.error);
|
||
if (ui.title) ui.title.textContent = `Preparing ${zipName}…`;
|
||
|
||
// --- RENDER PROGRESS ---
|
||
if (typeof s.pct === "number" && ui.bar && ui.text) {
|
||
if ((s.phase !== 'finalizing') && (s.pct < 99)) {
|
||
ui.hideSpinner && ui.hideSpinner();
|
||
const filesDone = s.filesDone ?? 0;
|
||
const filesTotal = s.filesTotal ?? 0;
|
||
const bytesDone = s.bytesDone ?? 0;
|
||
const bytesTotal = s.bytesTotal ?? 0;
|
||
|
||
// Determinate 0–98% while enumerating
|
||
const pct = Math.max(0, Math.min(98, s.pct | 0));
|
||
if (!ui.bar.hasAttribute("value")) ui.bar.value = 0;
|
||
ui.bar.value = pct;
|
||
ui.text.textContent =
|
||
`${pct}% — ${filesDone}/${filesTotal} files, ${humanBytes(bytesDone)} / ${humanBytes(bytesTotal)}`;
|
||
} else {
|
||
// FINALIZING: keep progress at 100% and show timer + selected totals
|
||
if (!ui.bar.hasAttribute("value")) ui.bar.value = 100;
|
||
ui.bar.value = 100; // lock at 100 during finalizing
|
||
const since = s.finalizeAt ? Math.max(0, (Date.now() / 1000 | 0) - (s.finalizeAt | 0)) : 0;
|
||
const selF = s.selectedFiles ?? s.filesTotal ?? 0;
|
||
const selB = s.selectedBytes ?? s.bytesTotal ?? 0;
|
||
ui.text.textContent = `Finalizing… ${mmss(since)} — ${selF} file${selF === 1 ? '' : 's'}, ~${humanBytes(selB)}`;
|
||
}
|
||
} else if (ui.text) {
|
||
ui.text.textContent = "Still preparing…";
|
||
}
|
||
// --- /RENDER ---
|
||
|
||
if (s.ready) {
|
||
// Snap to 100 and close modal just before download
|
||
if (ui.bar) { ui.bar.max = 100; ui.bar.value = 100; }
|
||
progressModal.style.display = "none";
|
||
await new Promise(r => setTimeout(r, 0));
|
||
break;
|
||
}
|
||
if (Date.now() - t0 > 15 * 60 * 1000) throw new Error("Timed out preparing ZIP");
|
||
}
|
||
|
||
// f) Trigger download
|
||
const a = document.createElement("a");
|
||
a.href = downloadUrl;
|
||
a.download = zipName;
|
||
a.style.display = "none";
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
a.remove();
|
||
|
||
// g) Reset for next time
|
||
if (ui.bar) ui.bar.value = 0;
|
||
if (ui.text) ui.text.textContent = "";
|
||
if (Array.isArray(window.filesToDownload)) window.filesToDownload = [];
|
||
});
|
||
}
|
||
});
|
||
|
||
export function handleCopySelected(e) {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
const checkboxes = document.querySelectorAll(".file-checkbox:checked");
|
||
if (checkboxes.length === 0) {
|
||
showToast("No files selected for copying.", 5000);
|
||
return;
|
||
}
|
||
window.filesToCopy = Array.from(checkboxes).map(chk => chk.value);
|
||
document.getElementById("copyFilesModal").style.display = "block";
|
||
loadCopyMoveFolderListForModal("copyTargetFolder");
|
||
}
|
||
|
||
export async function loadCopyMoveFolderListForModal(dropdownId) {
|
||
const folderSelect = document.getElementById(dropdownId);
|
||
folderSelect.innerHTML = "";
|
||
|
||
if (window.userFolderOnly) {
|
||
const username = localStorage.getItem("username") || "root";
|
||
try {
|
||
const response = await fetch("/api/folder/getFolderList.php?restricted=1");
|
||
let folders = await response.json();
|
||
if (Array.isArray(folders) && folders.length && typeof folders[0] === "object" && folders[0].folder) {
|
||
folders = folders.map(item => item.folder);
|
||
}
|
||
folders = folders.filter(folder =>
|
||
folder.toLowerCase() !== "trash" &&
|
||
(folder === username || folder.indexOf(username + "/") === 0)
|
||
);
|
||
|
||
const rootOption = document.createElement("option");
|
||
rootOption.value = username;
|
||
rootOption.textContent = formatFolderName(username);
|
||
folderSelect.appendChild(rootOption);
|
||
|
||
folders.forEach(folder => {
|
||
if (folder !== username) {
|
||
const option = document.createElement("option");
|
||
option.value = folder;
|
||
option.textContent = formatFolderName(folder);
|
||
folderSelect.appendChild(option);
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error("Error loading folder list for modal:", error);
|
||
}
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch("/api/folder/getFolderList.php");
|
||
let folders = await response.json();
|
||
if (Array.isArray(folders) && folders.length && typeof folders[0] === "object" && folders[0].folder) {
|
||
folders = folders.map(item => item.folder);
|
||
}
|
||
folders = folders.filter(folder => folder !== "root" && folder.toLowerCase() !== "trash");
|
||
|
||
const rootOption = document.createElement("option");
|
||
rootOption.value = "root";
|
||
rootOption.textContent = "(Root)";
|
||
folderSelect.appendChild(rootOption);
|
||
|
||
if (Array.isArray(folders) && folders.length > 0) {
|
||
folders.forEach(folder => {
|
||
const option = document.createElement("option");
|
||
option.value = folder;
|
||
option.textContent = folder;
|
||
folderSelect.appendChild(option);
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error("Error loading folder list for modal:", error);
|
||
}
|
||
}
|
||
|
||
export function handleMoveSelected(e) {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
const checkboxes = document.querySelectorAll(".file-checkbox:checked");
|
||
if (checkboxes.length === 0) {
|
||
showToast("No files selected for moving.");
|
||
return;
|
||
}
|
||
window.filesToMove = Array.from(checkboxes).map(chk => chk.value);
|
||
document.getElementById("moveFilesModal").style.display = "block";
|
||
loadCopyMoveFolderListForModal("moveTargetFolder");
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
const cancelCopy = document.getElementById("cancelCopyFiles");
|
||
if (cancelCopy) {
|
||
cancelCopy.addEventListener("click", function () {
|
||
document.getElementById("copyFilesModal").style.display = "none";
|
||
window.filesToCopy = [];
|
||
});
|
||
}
|
||
const confirmCopy = document.getElementById("confirmCopyFiles");
|
||
if (confirmCopy) {
|
||
confirmCopy.setAttribute("data-default", "");
|
||
confirmCopy.addEventListener("click", function () {
|
||
const targetFolder = document.getElementById("copyTargetFolder").value;
|
||
if (!targetFolder) {
|
||
showToast("Please select a target folder for copying.", 5000);
|
||
return;
|
||
}
|
||
if (targetFolder === window.currentFolder) {
|
||
showToast("Error: Cannot copy files to the same folder.");
|
||
return;
|
||
}
|
||
fetch("/api/file/copyFiles.php", {
|
||
method: "POST",
|
||
credentials: "include",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"X-CSRF-Token": window.csrfToken
|
||
},
|
||
body: JSON.stringify({
|
||
source: window.currentFolder,
|
||
files: window.filesToCopy,
|
||
destination: targetFolder
|
||
})
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast("Selected files copied successfully!", 5000);
|
||
loadFileList(window.currentFolder);
|
||
refreshFolderIcon(targetFolder);
|
||
} else {
|
||
showToast("Error: " + (data.error || "Could not copy files"), 5000);
|
||
}
|
||
})
|
||
.catch(error => console.error("Error copying files:", error))
|
||
.finally(() => {
|
||
document.getElementById("copyFilesModal").style.display = "none";
|
||
window.filesToCopy = [];
|
||
});
|
||
});
|
||
}
|
||
});
|
||
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
const cancelMove = document.getElementById("cancelMoveFiles");
|
||
if (cancelMove) {
|
||
cancelMove.addEventListener("click", function () {
|
||
document.getElementById("moveFilesModal").style.display = "none";
|
||
window.filesToMove = [];
|
||
});
|
||
}
|
||
const confirmMove = document.getElementById("confirmMoveFiles");
|
||
if (confirmMove) {
|
||
confirmMove.setAttribute("data-default", "");
|
||
confirmMove.addEventListener("click", function () {
|
||
const targetFolder = document.getElementById("moveTargetFolder").value;
|
||
if (!targetFolder) {
|
||
showToast("Please select a target folder for moving.");
|
||
return;
|
||
}
|
||
if (targetFolder === window.currentFolder) {
|
||
showToast("Error: Cannot move files to the same folder.");
|
||
return;
|
||
}
|
||
fetch("/api/file/moveFiles.php", {
|
||
method: "POST",
|
||
credentials: "include",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"X-CSRF-Token": window.csrfToken
|
||
},
|
||
body: JSON.stringify({
|
||
source: window.currentFolder,
|
||
files: window.filesToMove,
|
||
destination: targetFolder
|
||
})
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast("Selected files moved successfully!");
|
||
loadFileList(window.currentFolder);
|
||
refreshFolderIcon(targetFolder);
|
||
refreshFolderIcon(window.currentFolder);
|
||
} else {
|
||
showToast("Error: " + (data.error || "Could not move files"));
|
||
}
|
||
})
|
||
.catch(error => console.error("Error moving files:", error))
|
||
.finally(() => {
|
||
document.getElementById("moveFilesModal").style.display = "none";
|
||
window.filesToMove = [];
|
||
});
|
||
});
|
||
}
|
||
});
|
||
|
||
export function renameFile(oldName, folder) {
|
||
window.fileToRename = oldName;
|
||
window.fileFolder = folder || window.currentFolder || "root";
|
||
document.getElementById("newFileName").value = oldName;
|
||
document.getElementById("renameFileModal").style.display = "block";
|
||
setTimeout(() => {
|
||
const input = document.getElementById("newFileName");
|
||
input.focus();
|
||
const lastDot = oldName.lastIndexOf('.');
|
||
if (lastDot > 0) {
|
||
input.setSelectionRange(0, lastDot);
|
||
} else {
|
||
input.select();
|
||
}
|
||
}, 100);
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const cancelBtn = document.getElementById("cancelRenameFile");
|
||
if (cancelBtn) {
|
||
cancelBtn.addEventListener("click", function () {
|
||
document.getElementById("renameFileModal").style.display = "none";
|
||
document.getElementById("newFileName").value = "";
|
||
});
|
||
}
|
||
|
||
const submitBtn = document.getElementById("submitRenameFile");
|
||
if (submitBtn) {
|
||
submitBtn.setAttribute("data-default", "");
|
||
submitBtn.addEventListener("click", function () {
|
||
const newName = document.getElementById("newFileName").value.trim();
|
||
if (!newName || newName === window.fileToRename) {
|
||
document.getElementById("renameFileModal").style.display = "none";
|
||
return;
|
||
}
|
||
const folderUsed = window.fileFolder;
|
||
fetch("/api/file/renameFile.php", {
|
||
method: "POST",
|
||
credentials: "include",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"X-CSRF-Token": window.csrfToken
|
||
},
|
||
body: JSON.stringify({ folder: folderUsed, oldName: window.fileToRename, newName: newName })
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showToast("File renamed successfully!");
|
||
loadFileList(folderUsed);
|
||
} else {
|
||
showToast("Error renaming file: " + (data.error || "Unknown error"));
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error("Error renaming file:", error);
|
||
showToast("Error renaming file");
|
||
})
|
||
.finally(() => {
|
||
document.getElementById("renameFileModal").style.display = "none";
|
||
document.getElementById("newFileName").value = "";
|
||
});
|
||
});
|
||
}
|
||
});
|
||
|
||
// Expose initFileActions so it can be called from fileManager.js
|
||
export function initFileActions() {
|
||
portalFileModalsToBody();
|
||
const deleteSelectedBtn = document.getElementById("deleteSelectedBtn");
|
||
if (deleteSelectedBtn) {
|
||
deleteSelectedBtn.replaceWith(deleteSelectedBtn.cloneNode(true));
|
||
document.getElementById("deleteSelectedBtn").addEventListener("click", handleDeleteSelected);
|
||
}
|
||
const copySelectedBtn = document.getElementById("copySelectedBtn");
|
||
if (copySelectedBtn) {
|
||
copySelectedBtn.replaceWith(copySelectedBtn.cloneNode(true));
|
||
document.getElementById("copySelectedBtn").addEventListener("click", handleCopySelected);
|
||
}
|
||
const moveSelectedBtn = document.getElementById("moveSelectedBtn");
|
||
if (moveSelectedBtn) {
|
||
moveSelectedBtn.replaceWith(moveSelectedBtn.cloneNode(true));
|
||
document.getElementById("moveSelectedBtn").addEventListener("click", handleMoveSelected);
|
||
}
|
||
const downloadZipBtn = document.getElementById("downloadZipBtn");
|
||
if (downloadZipBtn) {
|
||
downloadZipBtn.replaceWith(downloadZipBtn.cloneNode(true));
|
||
document.getElementById("downloadZipBtn").addEventListener("click", handleDownloadZipSelected);
|
||
}
|
||
const extractZipBtn = document.getElementById("extractZipBtn");
|
||
if (extractZipBtn) {
|
||
extractZipBtn.replaceWith(extractZipBtn.cloneNode(true));
|
||
document.getElementById("extractZipBtn").addEventListener("click", handleExtractZipSelected);
|
||
}
|
||
const createBtn = document.getElementById('createFileBtn');
|
||
if (createBtn) {
|
||
createBtn.replaceWith(createBtn.cloneNode(true));
|
||
document.getElementById('createFileBtn').addEventListener('click', openCreateFileModal);
|
||
}
|
||
}
|
||
|
||
|
||
// Hook up the single‐file download modal buttons
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const cancelDownloadFileBtn = document.getElementById("cancelDownloadFile");
|
||
if (cancelDownloadFileBtn) {
|
||
cancelDownloadFileBtn.addEventListener("click", () => {
|
||
document.getElementById("downloadFileModal").style.display = "none";
|
||
});
|
||
}
|
||
|
||
const confirmSingleDownloadBtn = document.getElementById("confirmSingleDownloadButton");
|
||
if (confirmSingleDownloadBtn) {
|
||
confirmSingleDownloadBtn.addEventListener("click", confirmSingleDownload);
|
||
}
|
||
|
||
// Make Enter also confirm the download
|
||
attachEnterKeyListener("downloadFileModal", "confirmSingleDownloadButton");
|
||
});
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const btn = document.getElementById('createBtn');
|
||
const menu = document.getElementById('createMenu');
|
||
const fileOpt = document.getElementById('createFileOption');
|
||
const folderOpt = document.getElementById('createFolderOption');
|
||
const uploadOpt = document.getElementById('uploadOption'); // NEW
|
||
|
||
// Toggle dropdown on click
|
||
btn.addEventListener('click', (e) => {
|
||
e.stopPropagation();
|
||
menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
|
||
});
|
||
|
||
// Create File
|
||
fileOpt.addEventListener('click', () => {
|
||
menu.style.display = 'none';
|
||
openCreateFileModal(); // your existing function
|
||
});
|
||
|
||
// Create Folder
|
||
folderOpt.addEventListener('click', () => {
|
||
menu.style.display = 'none';
|
||
document.getElementById('createFolderModal').style.display = 'block';
|
||
document.getElementById('newFolderName').focus();
|
||
});
|
||
|
||
// Close if you click anywhere else
|
||
document.addEventListener('click', () => {
|
||
menu.style.display = 'none';
|
||
});
|
||
if (uploadOpt) {
|
||
uploadOpt.addEventListener('click', () => {
|
||
if (menu) menu.style.display = 'none';
|
||
openUploadModal();
|
||
});
|
||
}
|
||
|
||
// Close buttons / backdrop
|
||
const upModal = document.getElementById('uploadModal');
|
||
const closeX = document.getElementById('closeUploadModal');
|
||
|
||
if (closeX) closeX.addEventListener('click', closeUploadModal);
|
||
|
||
// click outside content to close
|
||
if (upModal) {
|
||
upModal.addEventListener('click', (e) => {
|
||
if (e.target === upModal) closeUploadModal();
|
||
});
|
||
}
|
||
|
||
// ESC to close
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Escape' && upModal && upModal.style.display === 'block') {
|
||
closeUploadModal();
|
||
}
|
||
});
|
||
});
|
||
|
||
window.renameFile = renameFile; |