some refactoring
This commit is contained in:
129
auth.js
129
auth.js
@@ -1,62 +1,79 @@
|
|||||||
|
// auth.js
|
||||||
|
import { sendRequest, toggleVisibility } from './utils.js';
|
||||||
|
|
||||||
|
let setupMode = false; // Declare setupMode here
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
// Hide file list and upload form on load
|
// Hide file list and upload form on load.
|
||||||
document.getElementById("fileListContainer").style.display = "none";
|
toggleVisibility("fileListContainer", false);
|
||||||
document.getElementById("uploadFileForm").style.display = "none";
|
toggleVisibility("uploadFileForm", false);
|
||||||
|
|
||||||
checkAuthentication();
|
checkAuthentication();
|
||||||
|
|
||||||
document.getElementById("authForm").addEventListener("submit", function (event) {
|
document.getElementById("authForm").addEventListener("submit", function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
const formData = {
|
||||||
const formData = {
|
username: document.getElementById("loginUsername").value.trim(),
|
||||||
username: document.getElementById("loginUsername").value.trim(),
|
password: document.getElementById("loginPassword").value.trim()
|
||||||
password: document.getElementById("loginPassword").value.trim()
|
};
|
||||||
};
|
console.log("Sending login data:", formData);
|
||||||
|
sendRequest("auth.php", "POST", formData)
|
||||||
console.log("Sending login data:", formData);
|
.then(data => {
|
||||||
|
console.log("Login response:", data);
|
||||||
fetch("auth.php", {
|
if (data.success) {
|
||||||
method: "POST",
|
console.log("Login successful.");
|
||||||
headers: { "Content-Type": "application/json" },
|
toggleVisibility("loginForm", false);
|
||||||
body: JSON.stringify(formData)
|
toggleVisibility("uploadFileForm", true);
|
||||||
})
|
toggleVisibility("fileListContainer", true);
|
||||||
.then(response => response.json())
|
checkAuthentication(); // Recheck authentication to update UI.
|
||||||
.then(data => {
|
} else {
|
||||||
console.log("Login response:", data);
|
alert("Login failed: " + (data.error || "Unknown error"));
|
||||||
if (data.success) {
|
}
|
||||||
console.log("Login successful.");
|
})
|
||||||
document.getElementById("loginForm").style.display = "none";
|
.catch(error => console.error("Error logging in:", error));
|
||||||
document.getElementById("uploadFileForm").style.display = "block";
|
});
|
||||||
document.getElementById("fileListContainer").style.display = "block";
|
|
||||||
checkAuthentication(); // Recheck authentication to show the file list
|
|
||||||
} else {
|
|
||||||
alert("Login failed: " + (data.error || "Unknown error"));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error logging in:", error));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkAuthentication() {
|
export function checkAuthentication() {
|
||||||
fetch("checkAuth.php")
|
sendRequest("checkAuth.php")
|
||||||
.then(response => response.json())
|
.then(data => {
|
||||||
.then(data => {
|
console.log("Authentication check:", data);
|
||||||
console.log("Authentication check:", data);
|
if (data.setup) {
|
||||||
if (data.authenticated) {
|
setupMode = true;
|
||||||
console.log("User authenticated, showing file list.");
|
// In setup mode, hide all sections except the Add User modal.
|
||||||
document.getElementById("loginForm").style.display = "none";
|
toggleVisibility("loginForm", false);
|
||||||
document.getElementById("uploadFileForm").style.display = "block";
|
toggleVisibility("uploadFileForm", false);
|
||||||
document.getElementById("fileListContainer").style.display = "block";
|
toggleVisibility("fileListContainer", false);
|
||||||
loadFileList();
|
document.querySelector(".header-buttons").style.visibility = "hidden";
|
||||||
} else {
|
toggleVisibility("addUserModal", true);
|
||||||
// Only log a warning if the file list is supposed to be shown (i.e. after a login)
|
return;
|
||||||
if (document.getElementById("uploadFileForm").style.display === "block") {
|
} else {
|
||||||
console.warn("User not authenticated.");
|
setupMode = false;
|
||||||
}
|
}
|
||||||
document.getElementById("loginForm").style.display = "block";
|
if (data.authenticated) {
|
||||||
document.getElementById("uploadFileForm").style.display = "none";
|
toggleVisibility("loginForm", false);
|
||||||
document.getElementById("fileListContainer").style.display = "none";
|
toggleVisibility("uploadFileForm", true);
|
||||||
}
|
toggleVisibility("fileListContainer", true);
|
||||||
})
|
if (typeof loadFileList === "function") {
|
||||||
.catch(error => console.error("Error checking authentication:", error));
|
loadFileList();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toggleVisibility("loginForm", true);
|
||||||
|
toggleVisibility("uploadFileForm", false);
|
||||||
|
toggleVisibility("fileListContainer", false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error checking authentication:", error));
|
||||||
|
}
|
||||||
|
window.checkAuthentication = checkAuthentication;
|
||||||
|
|
||||||
|
// Helper functions for the Add User modal.
|
||||||
|
function closeAddUserModal() {
|
||||||
|
toggleVisibility("addUserModal", false);
|
||||||
|
resetUserForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetUserForm() {
|
||||||
|
document.getElementById("newUsername").value = "";
|
||||||
|
document.getElementById("newPassword").value = "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,70 @@
|
|||||||
let fileData = []; // Store file data globally
|
// displayFileList.js
|
||||||
let sortOrder = { column: "uploaded", ascending: false }; // Default sorting by uploaded date (newest first)
|
import { sendRequest, toggleVisibility } from './utils.js';
|
||||||
|
|
||||||
// Load and display the file list
|
let fileData = [];
|
||||||
window.loadFileList = function () {
|
let sortOrder = { column: "uploaded", ascending: false };
|
||||||
fetch("checkAuth.php")
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(authData => {
|
|
||||||
if (!authData.authenticated) {
|
|
||||||
console.warn("User not authenticated, hiding file list.");
|
|
||||||
document.getElementById("fileListContainer").style.display = "none";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
document.getElementById("fileListContainer").style.display = "block";
|
|
||||||
fetch("getFileList.php")
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.error) {
|
|
||||||
document.getElementById("fileList").innerHTML = `<p style="color:red;">Error: ${data.error}</p>`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!Array.isArray(data.files)) {
|
|
||||||
console.error("Unexpected response format:", data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fileData = data.files; // Store file data globally
|
|
||||||
sortFiles("uploaded", false); // Sort by upload date (newest first) on load
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error fetching file list:", error));
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error checking authentication:", error));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sort files when clicking headers
|
export function loadFileList() {
|
||||||
function sortFiles(column, forceAscending = null) {
|
sendRequest("checkAuth.php")
|
||||||
if (sortOrder.column === column) {
|
.then(authData => {
|
||||||
sortOrder.ascending = forceAscending !== null ? forceAscending : !sortOrder.ascending; // Toggle order
|
if (!authData.authenticated) {
|
||||||
} else {
|
console.warn("User not authenticated, hiding file list.");
|
||||||
sortOrder.column = column;
|
toggleVisibility("fileListContainer", false);
|
||||||
sortOrder.ascending = forceAscending !== null ? forceAscending : true; // Default to ascending when switching column
|
return;
|
||||||
}
|
}
|
||||||
fileData.sort((a, b) => {
|
toggleVisibility("fileListContainer", true);
|
||||||
let valA = a[column] || "";
|
return sendRequest("getFileList.php");
|
||||||
let valB = b[column] || "";
|
})
|
||||||
if (typeof valA === "string") valA = valA.toLowerCase();
|
.then(data => {
|
||||||
if (typeof valB === "string") valB = valB.toLowerCase();
|
if (!data) return;
|
||||||
if (valA < valB) return sortOrder.ascending ? -1 : 1;
|
if (data.error) {
|
||||||
if (valA > valB) return sortOrder.ascending ? 1 : -1;
|
document.getElementById("fileList").innerHTML = `<p style="color:red;">Error: ${data.error}</p>`;
|
||||||
return 0;
|
return;
|
||||||
});
|
}
|
||||||
renderFileTable(); // Re-render table after sorting
|
if (!Array.isArray(data.files)) {
|
||||||
|
console.error("Unexpected response format:", data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileData = data.files;
|
||||||
|
sortFiles("uploaded", false);
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error loading file list:", error));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to render file table
|
export function sortFiles(column, forceAscending = null) {
|
||||||
function renderFileTable() {
|
if (sortOrder.column === column) {
|
||||||
const fileListContainer = document.getElementById("fileList");
|
sortOrder.ascending = forceAscending !== null ? forceAscending : !sortOrder.ascending;
|
||||||
let tableHTML = `<table class="table">
|
} else {
|
||||||
|
sortOrder.column = column;
|
||||||
|
sortOrder.ascending = forceAscending !== null ? forceAscending : true;
|
||||||
|
}
|
||||||
|
fileData.sort((a, b) => {
|
||||||
|
let valA = a[column] || "";
|
||||||
|
let valB = b[column] || "";
|
||||||
|
if (column === "modified" || column === "uploaded") {
|
||||||
|
const dateA = new Date(valA);
|
||||||
|
const dateB = new Date(valB);
|
||||||
|
if (!isNaN(dateA.getTime()) && !isNaN(dateB.getTime())) {
|
||||||
|
valA = dateA.getTime();
|
||||||
|
valB = dateB.getTime();
|
||||||
|
} else {
|
||||||
|
valA = valA.toLowerCase();
|
||||||
|
valB = valB.toLowerCase();
|
||||||
|
}
|
||||||
|
} else if (typeof valA === "string") {
|
||||||
|
valA = valA.toLowerCase();
|
||||||
|
valB = valB.toLowerCase();
|
||||||
|
}
|
||||||
|
if (valA < valB) return sortOrder.ascending ? -1 : 1;
|
||||||
|
if (valA > valB) return sortOrder.ascending ? 1 : -1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
renderFileTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderFileTable() {
|
||||||
|
const fileListContainer = document.getElementById("fileList");
|
||||||
|
let tableHTML = `<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><input type="checkbox" id="selectAll" onclick="toggleAllCheckboxes(this)"></th>
|
<th><input type="checkbox" id="selectAll" onclick="toggleAllCheckboxes(this)"></th>
|
||||||
@@ -67,8 +77,8 @@ function renderFileTable() {
|
|||||||
<th onclick="sortFiles('uploaded')" style="cursor:pointer; text-decoration: underline; white-space: nowrap;">
|
<th onclick="sortFiles('uploaded')" style="cursor:pointer; text-decoration: underline; white-space: nowrap;">
|
||||||
<span>Upload Date</span> <span>${sortOrder.column === "uploaded" ? (sortOrder.ascending ? "▲" : "▼") : ""}</span>
|
<span>Upload Date</span> <span>${sortOrder.column === "uploaded" ? (sortOrder.ascending ? "▲" : "▼") : ""}</span>
|
||||||
</th>
|
</th>
|
||||||
<th onclick="sortFiles('sizeBytes')" style="cursor:pointer; text-decoration: underline; white-space: nowrap;">
|
<th onclick="sortFiles('size')" style="cursor:pointer; text-decoration: underline; white-space: nowrap;">
|
||||||
<span>File Size</span> <span>${sortOrder.column === "sizeBytes" ? (sortOrder.ascending ? "▲" : "▼") : ""}</span>
|
<span>File Size</span> <span>${sortOrder.column === "size" ? (sortOrder.ascending ? "▲" : "▼") : ""}</span>
|
||||||
</th>
|
</th>
|
||||||
<th onclick="sortFiles('uploader')" style="cursor:pointer; text-decoration: underline; white-space: nowrap;">
|
<th onclick="sortFiles('uploader')" style="cursor:pointer; text-decoration: underline; white-space: nowrap;">
|
||||||
<span>Uploader</span> <span>${sortOrder.column === "uploader" ? (sortOrder.ascending ? "▲" : "▼") : ""}</span>
|
<span>Uploader</span> <span>${sortOrder.column === "uploader" ? (sortOrder.ascending ? "▲" : "▼") : ""}</span>
|
||||||
@@ -77,18 +87,18 @@ function renderFileTable() {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>`;
|
<tbody>`;
|
||||||
|
|
||||||
fileData.forEach(file => {
|
fileData.forEach(file => {
|
||||||
const isEditable = file.name.endsWith(".txt") || file.name.endsWith(".json") ||
|
const isEditable = file.name.endsWith(".txt") || file.name.endsWith(".json") ||
|
||||||
file.name.endsWith(".ini") || file.name.endsWith(".css") ||
|
file.name.endsWith(".ini") || file.name.endsWith(".css") ||
|
||||||
file.name.endsWith(".js") || file.name.endsWith(".csv") ||
|
file.name.endsWith(".js") || file.name.endsWith(".csv") ||
|
||||||
file.name.endsWith(".md") || file.name.endsWith(".xml") ||
|
file.name.endsWith(".md") || file.name.endsWith(".xml") ||
|
||||||
file.name.endsWith(".html") || file.name.endsWith(".py") ||
|
file.name.endsWith(".html") || file.name.endsWith(".py") ||
|
||||||
file.name.endsWith(".log") || file.name.endsWith(".conf") ||
|
file.name.endsWith(".log") || file.name.endsWith(".conf") ||
|
||||||
file.name.endsWith(".config") || file.name.endsWith(".bat") ||
|
file.name.endsWith(".config") || file.name.endsWith(".bat") ||
|
||||||
file.name.endsWith(".rtf") || file.name.endsWith(".doc") ||
|
file.name.endsWith(".rtf") || file.name.endsWith(".doc") ||
|
||||||
file.name.endsWith(".docx");
|
file.name.endsWith(".docx");
|
||||||
tableHTML += `<tr>
|
tableHTML += `<tr>
|
||||||
<td><input type="checkbox" class="file-checkbox" value="${file.name}" onclick="toggleDeleteButton()"></td>
|
<td><input type="checkbox" class="file-checkbox" value="${file.name}" onclick="toggleDeleteButton()"></td>
|
||||||
<td>${file.name}</td>
|
<td>${file.name}</td>
|
||||||
<td style="white-space: nowrap;">${file.modified}</td>
|
<td style="white-space: nowrap;">${file.modified}</td>
|
||||||
@@ -102,73 +112,60 @@ function renderFileTable() {
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
tableHTML += `</tbody></table>`;
|
tableHTML += `</tbody></table>`;
|
||||||
fileListContainer.innerHTML = tableHTML;
|
fileListContainer.innerHTML = tableHTML;
|
||||||
|
|
||||||
// Always display the batch delete button if there are files; disable it if no file is selected.
|
const deleteBtn = document.getElementById("deleteSelectedBtn");
|
||||||
const deleteBtn = document.getElementById("deleteSelectedBtn");
|
if (fileData.length > 0) {
|
||||||
if (fileData.length > 0) {
|
deleteBtn.style.display = "block";
|
||||||
deleteBtn.style.display = "block";
|
|
||||||
// Check if any checkboxes are selected to enable the button; if none, disable it.
|
|
||||||
const selectedFiles = document.querySelectorAll(".file-checkbox:checked");
|
|
||||||
deleteBtn.disabled = selectedFiles.length === 0;
|
|
||||||
} else {
|
|
||||||
deleteBtn.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to toggle delete button enabled state
|
|
||||||
function toggleDeleteButton() {
|
|
||||||
const selectedFiles = document.querySelectorAll(".file-checkbox:checked");
|
const selectedFiles = document.querySelectorAll(".file-checkbox:checked");
|
||||||
const deleteBtn = document.getElementById("deleteSelectedBtn");
|
|
||||||
deleteBtn.disabled = selectedFiles.length === 0;
|
deleteBtn.disabled = selectedFiles.length === 0;
|
||||||
|
} else {
|
||||||
|
deleteBtn.style.display = "none";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select/Deselect All Checkboxes
|
export function toggleDeleteButton() {
|
||||||
window.toggleAllCheckboxes = function (source) {
|
const selectedFiles = document.querySelectorAll(".file-checkbox:checked");
|
||||||
const checkboxes = document.querySelectorAll(".file-checkbox");
|
const deleteBtn = document.getElementById("deleteSelectedBtn");
|
||||||
checkboxes.forEach(checkbox => checkbox.checked = source.checked);
|
deleteBtn.disabled = selectedFiles.length === 0;
|
||||||
toggleDeleteButton();
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Batch delete function
|
export function toggleAllCheckboxes(source) {
|
||||||
window.deleteSelectedFiles = function () {
|
const checkboxes = document.querySelectorAll(".file-checkbox");
|
||||||
const selectedFiles = Array.from(document.querySelectorAll(".file-checkbox:checked"))
|
checkboxes.forEach(checkbox => checkbox.checked = source.checked);
|
||||||
.map(checkbox => checkbox.value);
|
toggleDeleteButton();
|
||||||
if (selectedFiles.length === 0) {
|
}
|
||||||
alert("No files selected for deletion.");
|
|
||||||
return;
|
export function deleteSelectedFiles() {
|
||||||
}
|
const selectedFiles = Array.from(document.querySelectorAll(".file-checkbox:checked"))
|
||||||
if (!confirm(`Are you sure you want to delete the selected files?`)) {
|
.map(checkbox => checkbox.value);
|
||||||
return;
|
if (selectedFiles.length === 0) {
|
||||||
}
|
alert("No files selected for deletion.");
|
||||||
fetch("deleteFiles.php", {
|
return;
|
||||||
method: "POST",
|
}
|
||||||
headers: { "Content-Type": "application/json" },
|
if (!confirm("Are you sure you want to delete the selected files?")) {
|
||||||
body: JSON.stringify({ files: selectedFiles })
|
return;
|
||||||
})
|
}
|
||||||
.then(response => response.json())
|
sendRequest("deleteFiles.php", "POST", { files: selectedFiles })
|
||||||
.then(result => {
|
.then(result => {
|
||||||
alert(result.success || result.error);
|
alert(result.success || result.error);
|
||||||
loadFileList();
|
loadFileList();
|
||||||
})
|
})
|
||||||
.catch(error => console.error("Error deleting files:", error));
|
.catch(error => console.error("Error deleting files:", error));
|
||||||
};
|
}
|
||||||
|
|
||||||
// Attach event listener to batch delete button
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
const deleteBtn = document.getElementById("deleteSelectedBtn");
|
const deleteBtn = document.getElementById("deleteSelectedBtn");
|
||||||
if (deleteBtn) {
|
if (deleteBtn) {
|
||||||
deleteBtn.addEventListener("click", deleteSelectedFiles);
|
deleteBtn.addEventListener("click", deleteSelectedFiles);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.editFile = function(fileName) {
|
export function editFile(fileName) {
|
||||||
const threshold = 10 * 1024 * 1024; // 10 MB threshold
|
const threshold = 10 * 1024 * 1024; // 10 MB threshold
|
||||||
|
|
||||||
// Check file size via HEAD request.
|
|
||||||
fetch(`uploads/${encodeURIComponent(fileName)}`, { method: 'HEAD' })
|
fetch(`uploads/${encodeURIComponent(fileName)}`, { method: 'HEAD' })
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const fileSize = parseInt(response.headers.get('Content-Length') || "0", 10);
|
const fileSize = parseInt(response.headers.get('Content-Length') || "0", 10);
|
||||||
@@ -187,7 +184,6 @@ window.editFile = function(fileName) {
|
|||||||
if (!content) return;
|
if (!content) return;
|
||||||
const modal = document.createElement("div");
|
const modal = document.createElement("div");
|
||||||
modal.id = "editorContainer";
|
modal.id = "editorContainer";
|
||||||
// Use your existing classes for styling.
|
|
||||||
modal.classList.add("modal", "editor-modal");
|
modal.classList.add("modal", "editor-modal");
|
||||||
modal.innerHTML = `
|
modal.innerHTML = `
|
||||||
<h3>Editing: ${fileName}</h3>
|
<h3>Editing: ${fileName}</h3>
|
||||||
@@ -201,28 +197,32 @@ window.editFile = function(fileName) {
|
|||||||
modal.style.display = "block";
|
modal.style.display = "block";
|
||||||
})
|
})
|
||||||
.catch(error => console.error("Error in editFile:", error));
|
.catch(error => console.error("Error in editFile:", error));
|
||||||
};
|
}
|
||||||
|
|
||||||
window.saveFile = function(fileName) {
|
export function saveFile(fileName) {
|
||||||
const editor = document.getElementById("fileEditor");
|
const editor = document.getElementById("fileEditor");
|
||||||
if (!editor) {
|
if (!editor) {
|
||||||
console.error("Editor not found!");
|
console.error("Editor not found!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const fileData = {
|
const fileData = {
|
||||||
fileName: fileName,
|
fileName: fileName,
|
||||||
content: editor.value
|
content: editor.value
|
||||||
};
|
};
|
||||||
fetch("saveFile.php", {
|
sendRequest("saveFile.php", "POST", fileData)
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(fileData)
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => {
|
.then(result => {
|
||||||
alert(result.success || result.error);
|
alert(result.success || result.error);
|
||||||
document.getElementById("editorContainer")?.remove();
|
document.getElementById("editorContainer")?.remove();
|
||||||
loadFileList();
|
loadFileList();
|
||||||
})
|
})
|
||||||
.catch(error => console.error("Error saving file:", error));
|
.catch(error => console.error("Error saving file:", error));
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// To support inline onclick attributes in the generated HTML, attach these functions to window.
|
||||||
|
window.sortFiles = sortFiles;
|
||||||
|
window.toggleDeleteButton = toggleDeleteButton;
|
||||||
|
window.toggleAllCheckboxes = toggleAllCheckboxes;
|
||||||
|
window.deleteSelectedFiles = deleteSelectedFiles;
|
||||||
|
window.editFile = editFile;
|
||||||
|
window.saveFile = saveFile;
|
||||||
|
window.loadFileList = loadFileList;
|
||||||
|
|||||||
646
index.html
646
index.html
@@ -1,6 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
@@ -15,9 +14,11 @@
|
|||||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<!-- Scripts (order is important) -->
|
<!-- Scripts (order is important) -->
|
||||||
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||||
<script src="auth.js"></script>
|
<script type="module" src="utils.js"></script>
|
||||||
<script src="upload.js"></script>
|
<script type="module" src="auth.js"></script>
|
||||||
<script src="displayFileList.js"></script>
|
<script type="module" src="upload.js"></script>
|
||||||
|
<script type="module" src="displayFileList.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* General styles */
|
/* General styles */
|
||||||
body {
|
body {
|
||||||
@@ -131,13 +132,11 @@
|
|||||||
.card {
|
.card {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
/* Keep action buttons on one line */
|
|
||||||
.actions-cell {
|
.actions-cell {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<header>
|
<header>
|
||||||
@@ -159,7 +158,6 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<!-- Login Form -->
|
<!-- Login Form -->
|
||||||
<div class="row" id="loginForm">
|
<div class="row" id="loginForm">
|
||||||
@@ -177,7 +175,6 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Main Operations: Upload and Folder Management -->
|
<!-- Main Operations: Upload and Folder Management -->
|
||||||
<div id="mainOperations" style="display: none;">
|
<div id="mainOperations" style="display: none;">
|
||||||
<div class="row" id="uploadFolderRow">
|
<div class="row" id="uploadFolderRow">
|
||||||
@@ -187,7 +184,6 @@
|
|||||||
<div class="card-header">Upload Files</div>
|
<div class="card-header">Upload Files</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form id="uploadFileForm" method="post" enctype="multipart/form-data">
|
<form id="uploadFileForm" method="post" enctype="multipart/form-data">
|
||||||
<!-- We'll use the global currentFolder -->
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="file">Choose files to upload:</label>
|
<label for="file">Choose files to upload:</label>
|
||||||
<input type="file" id="file" name="file[]" class="form-control-file" multiple required>
|
<input type="file" id="file" name="file[]" class="form-control-file" multiple required>
|
||||||
@@ -205,9 +201,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<button id="createFolderBtn" class="btn btn-primary mb-3">Create Folder</button>
|
<button id="createFolderBtn" class="btn btn-primary mb-3">Create Folder</button>
|
||||||
<div class="form-group d-flex align-items-center">
|
<div class="form-group d-flex align-items-center">
|
||||||
<select id="folderSelect" class="form-control">
|
<select id="folderSelect" class="form-control"></select>
|
||||||
<!-- (Root) and folders will be loaded dynamically -->
|
|
||||||
</select>
|
|
||||||
<button id="renameFolderBtn" class="btn btn-secondary ml-2" title="Rename Folder">
|
<button id="renameFolderBtn" class="btn btn-secondary ml-2" title="Rename Folder">
|
||||||
<i class="material-icons">edit</i>
|
<i class="material-icons">edit</i>
|
||||||
</button>
|
</button>
|
||||||
@@ -222,12 +216,10 @@
|
|||||||
<!-- File List Section -->
|
<!-- File List Section -->
|
||||||
<div id="fileListContainer">
|
<div id="fileListContainer">
|
||||||
<h2 id="fileListTitle">Files in (Root)</h2>
|
<h2 id="fileListTitle">Files in (Root)</h2>
|
||||||
<!-- Delete Selected button (initially hidden) -->
|
|
||||||
<button id="deleteSelectedBtn" class="btn btn-danger" style="margin-bottom: 10px; display: none;">Delete Selected</button>
|
<button id="deleteSelectedBtn" class="btn btn-danger" style="margin-bottom: 10px; display: none;">Delete Selected</button>
|
||||||
<div id="fileList"></div>
|
<div id="fileList"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Add User Modal -->
|
<!-- Add User Modal -->
|
||||||
<div id="addUserModal" class="modal">
|
<div id="addUserModal" class="modal">
|
||||||
<h3>Create New User</h3>
|
<h3>Create New User</h3>
|
||||||
@@ -242,7 +234,6 @@
|
|||||||
<button id="saveUserBtn" class="btn btn-primary">Save User</button>
|
<button id="saveUserBtn" class="btn btn-primary">Save User</button>
|
||||||
<button id="cancelUserBtn" class="btn btn-secondary">Cancel</button>
|
<button id="cancelUserBtn" class="btn btn-secondary">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Remove User Modal -->
|
<!-- Remove User Modal -->
|
||||||
<div id="removeUserModal" class="modal">
|
<div id="removeUserModal" class="modal">
|
||||||
<h3>Remove User</h3>
|
<h3>Remove User</h3>
|
||||||
@@ -252,630 +243,5 @@
|
|||||||
<button id="cancelRemoveUserBtn" class="btn btn-secondary">Cancel</button>
|
<button id="cancelRemoveUserBtn" class="btn btn-secondary">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
// Global variable to track current folder.
|
|
||||||
let currentFolder = "root";
|
|
||||||
// Global variable to track if we're in setup mode.
|
|
||||||
let setupMode = false;
|
|
||||||
|
|
||||||
// Helper: Determine if a file can be edited based on its extension.
|
|
||||||
function canEditFile(fileName) {
|
|
||||||
const allowedExtensions = ["txt", "html", "htm", "php", "css", "js", "json", "xml", "md", "py"];
|
|
||||||
const parts = fileName.split('.');
|
|
||||||
if (parts.length < 2) return false;
|
|
||||||
const ext = parts.pop().toLowerCase();
|
|
||||||
return allowedExtensions.includes(ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper: Display a file preview in the container.
|
|
||||||
function displayFilePreview(file, container) {
|
|
||||||
if (file.type.startsWith("image/")) {
|
|
||||||
const img = document.createElement("img");
|
|
||||||
img.style.width = "32px";
|
|
||||||
img.style.height = "32px";
|
|
||||||
img.style.objectFit = "cover";
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = function (e) {
|
|
||||||
img.src = e.target.result;
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
container.appendChild(img);
|
|
||||||
} else {
|
|
||||||
const icon = document.createElement("i");
|
|
||||||
icon.className = "material-icons";
|
|
||||||
icon.style.fontSize = "32px";
|
|
||||||
icon.textContent = "insert_drive_file";
|
|
||||||
container.appendChild(icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
checkAuthentication();
|
|
||||||
|
|
||||||
function updateUI(data) {
|
|
||||||
console.log("Auth data:", data);
|
|
||||||
// If in setup mode, set our global flag and show only the Add User modal.
|
|
||||||
if (data.setup) {
|
|
||||||
setupMode = true;
|
|
||||||
document.getElementById("loginForm").style.display = "none";
|
|
||||||
document.getElementById("mainOperations").style.display = "none";
|
|
||||||
document.getElementById("fileListContainer").style.display = "none";
|
|
||||||
document.querySelector(".header-buttons").style.visibility = "hidden";
|
|
||||||
document.getElementById("addUserModal").style.display = "block";
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
setupMode = false;
|
|
||||||
}
|
|
||||||
if (data.authenticated) {
|
|
||||||
document.getElementById("loginForm").style.display = "none";
|
|
||||||
document.getElementById("mainOperations").style.display = "block";
|
|
||||||
document.getElementById("fileListContainer").style.display = "block";
|
|
||||||
document.querySelector(".header-buttons").style.visibility = "visible";
|
|
||||||
if (data.isAdmin) {
|
|
||||||
document.getElementById("logoutBtn").style.display = "block";
|
|
||||||
document.getElementById("addUserBtn").style.display = "block";
|
|
||||||
document.getElementById("removeUserBtn").style.display = "block";
|
|
||||||
} else {
|
|
||||||
document.getElementById("logoutBtn").style.display = "block";
|
|
||||||
document.getElementById("addUserBtn").style.display = "none";
|
|
||||||
document.getElementById("removeUserBtn").style.display = "none";
|
|
||||||
}
|
|
||||||
loadFolderList();
|
|
||||||
} else {
|
|
||||||
document.getElementById("loginForm").style.display = "block";
|
|
||||||
document.getElementById("mainOperations").style.display = "none";
|
|
||||||
document.getElementById("fileListContainer").style.display = "none";
|
|
||||||
document.querySelector(".header-buttons").style.visibility = "hidden";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkAuthentication() {
|
|
||||||
fetch("checkAuth.php")
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(updateUI)
|
|
||||||
.catch(error => console.error("Error checking authentication:", error));
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("authForm").addEventListener("submit", function (event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const formData = {
|
|
||||||
username: document.getElementById("loginUsername").value.trim(),
|
|
||||||
password: document.getElementById("loginPassword").value.trim()
|
|
||||||
};
|
|
||||||
fetch("auth.php", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(formData)
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
updateUI({ authenticated: true, isAdmin: data.isAdmin });
|
|
||||||
} else {
|
|
||||||
alert("Login failed: " + (data.error || "Unknown error"));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error logging in:", error));
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById("logoutBtn").addEventListener("click", function () {
|
|
||||||
fetch("logout.php", { method: "POST" })
|
|
||||||
.then(() => window.location.reload(true))
|
|
||||||
.catch(error => console.error("Logout error:", error));
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- Add User Functionality ---
|
|
||||||
document.getElementById("addUserBtn").addEventListener("click", function () {
|
|
||||||
resetUserForm();
|
|
||||||
document.getElementById("addUserModal").style.display = "block";
|
|
||||||
});
|
|
||||||
document.getElementById("saveUserBtn").addEventListener("click", function () {
|
|
||||||
const newUsername = document.getElementById("newUsername").value.trim();
|
|
||||||
const newPassword = document.getElementById("newPassword").value.trim();
|
|
||||||
const isAdmin = document.getElementById("isAdmin").checked;
|
|
||||||
if (!newUsername || !newPassword) {
|
|
||||||
alert("Username and password are required!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If in setup mode, add the setup query parameter.
|
|
||||||
let url = "addUser.php";
|
|
||||||
if (setupMode) {
|
|
||||||
url += "?setup=1";
|
|
||||||
}
|
|
||||||
fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ username: newUsername, password: newPassword, isAdmin })
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
alert("User added successfully!");
|
|
||||||
closeAddUserModal();
|
|
||||||
checkAuthentication();
|
|
||||||
} else {
|
|
||||||
alert("Error: " + (data.error || "Could not add user"));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error adding user:", error));
|
|
||||||
});
|
|
||||||
document.getElementById("cancelUserBtn").addEventListener("click", function () {
|
|
||||||
closeAddUserModal();
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- Remove User Functionality ---
|
|
||||||
document.getElementById("removeUserBtn").addEventListener("click", function () {
|
|
||||||
loadUserList();
|
|
||||||
document.getElementById("removeUserModal").style.display = "block";
|
|
||||||
});
|
|
||||||
document.getElementById("deleteUserBtn").addEventListener("click", function () {
|
|
||||||
const selectElem = document.getElementById("removeUsernameSelect");
|
|
||||||
const usernameToRemove = selectElem.value;
|
|
||||||
if (!usernameToRemove) {
|
|
||||||
alert("Please select a user to remove.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!confirm("Are you sure you want to delete user " + usernameToRemove + "?")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetch("removeUser.php", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ username: usernameToRemove })
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
alert("User removed successfully!");
|
|
||||||
closeRemoveUserModal();
|
|
||||||
loadUserList();
|
|
||||||
} else {
|
|
||||||
alert("Error: " + (data.error || "Could not remove user"));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error removing user:", error));
|
|
||||||
});
|
|
||||||
document.getElementById("cancelRemoveUserBtn").addEventListener("click", function () {
|
|
||||||
closeRemoveUserModal();
|
|
||||||
});
|
|
||||||
function closeAddUserModal() {
|
|
||||||
document.getElementById("addUserModal").style.display = "none";
|
|
||||||
resetUserForm();
|
|
||||||
}
|
|
||||||
function resetUserForm() {
|
|
||||||
document.getElementById("newUsername").value = "";
|
|
||||||
document.getElementById("newPassword").value = "";
|
|
||||||
}
|
|
||||||
function closeRemoveUserModal() {
|
|
||||||
document.getElementById("removeUserModal").style.display = "none";
|
|
||||||
document.getElementById("removeUsernameSelect").innerHTML = "";
|
|
||||||
}
|
|
||||||
function loadUserList() {
|
|
||||||
fetch("getUsers.php")
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
// Accept either an array directly or in data.users
|
|
||||||
const users = Array.isArray(data) ? data : (data.users || []);
|
|
||||||
if (!users || !Array.isArray(users)) {
|
|
||||||
console.error("Invalid users data:", data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const selectElem = document.getElementById("removeUsernameSelect");
|
|
||||||
selectElem.innerHTML = "";
|
|
||||||
users.forEach(user => {
|
|
||||||
const option = document.createElement("option");
|
|
||||||
option.value = user.username;
|
|
||||||
option.textContent = user.username;
|
|
||||||
selectElem.appendChild(option);
|
|
||||||
});
|
|
||||||
if (selectElem.options.length === 0) {
|
|
||||||
alert("No other users found to remove.");
|
|
||||||
closeRemoveUserModal();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error loading user list:", error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folder Management functions.
|
|
||||||
function loadFolderList(selectedFolder) {
|
|
||||||
const folderSelect = document.getElementById("folderSelect");
|
|
||||||
folderSelect.innerHTML = "";
|
|
||||||
const rootOption = document.createElement("option");
|
|
||||||
rootOption.value = "root";
|
|
||||||
rootOption.textContent = "(Root)";
|
|
||||||
folderSelect.appendChild(rootOption);
|
|
||||||
fetch("getFolderList.php")
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(folders => {
|
|
||||||
folders.forEach(function (folder) {
|
|
||||||
let option = document.createElement("option");
|
|
||||||
option.value = folder;
|
|
||||||
option.textContent = folder;
|
|
||||||
folderSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
if (selectedFolder && [...folderSelect.options].some(opt => opt.value === selectedFolder)) {
|
|
||||||
folderSelect.value = selectedFolder;
|
|
||||||
} else {
|
|
||||||
folderSelect.value = "root";
|
|
||||||
}
|
|
||||||
currentFolder = folderSelect.value; // update global variable
|
|
||||||
document.getElementById("fileListTitle").textContent =
|
|
||||||
currentFolder === "root" ? "Files in (Root)" : "Files in (" + currentFolder + ")";
|
|
||||||
loadFileList(currentFolder);
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error loading folder list:", error));
|
|
||||||
}
|
|
||||||
document.getElementById("folderSelect").addEventListener("change", function () {
|
|
||||||
currentFolder = this.value;
|
|
||||||
document.getElementById("fileListTitle").textContent =
|
|
||||||
currentFolder === "root" ? "Files in (Root)" : "Files in (" + currentFolder + ")";
|
|
||||||
loadFileList(currentFolder);
|
|
||||||
});
|
|
||||||
document.getElementById("createFolderBtn").addEventListener("click", function () {
|
|
||||||
let folderName = prompt("Enter folder name:");
|
|
||||||
if (folderName) {
|
|
||||||
fetch("createFolder.php", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ folder: folderName })
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
alert("Folder created successfully!");
|
|
||||||
loadFolderList(folderName);
|
|
||||||
} else {
|
|
||||||
alert("Error: " + (data.error || "Could not create folder"));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error creating folder:", error));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
document.getElementById("renameFolderBtn").addEventListener("click", function () {
|
|
||||||
const folderSelect = document.getElementById("folderSelect");
|
|
||||||
const selectedFolder = folderSelect.value;
|
|
||||||
if (!selectedFolder || selectedFolder === "root") {
|
|
||||||
alert("Please select a valid folder to rename.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let newFolderName = prompt("Enter new folder name for '" + selectedFolder + "':", selectedFolder);
|
|
||||||
if (newFolderName && newFolderName !== selectedFolder) {
|
|
||||||
fetch("renameFolder.php", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ oldFolder: selectedFolder, newFolder: newFolderName })
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
alert("Folder renamed successfully!");
|
|
||||||
loadFolderList(newFolderName);
|
|
||||||
} else {
|
|
||||||
alert("Error: " + (data.error || "Could not rename folder"));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error renaming folder:", error));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
document.getElementById("deleteFolderBtn").addEventListener("click", function () {
|
|
||||||
const folderSelect = document.getElementById("folderSelect");
|
|
||||||
const selectedFolder = folderSelect.value;
|
|
||||||
if (!selectedFolder || selectedFolder === "root") {
|
|
||||||
alert("Please select a valid folder to delete.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (confirm("Are you sure you want to delete folder " + selectedFolder + "?")) {
|
|
||||||
fetch("deleteFolder.php", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ folder: selectedFolder })
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
alert("Folder deleted successfully!");
|
|
||||||
loadFolderList("root");
|
|
||||||
} else {
|
|
||||||
alert("Error: " + (data.error || "Could not delete folder"));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error deleting folder:", error));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// loadFileList function accepts a folder parameter.
|
|
||||||
function loadFileList(folderParam) {
|
|
||||||
const folder = folderParam || currentFolder || "root";
|
|
||||||
fetch("getFileList.php?folder=" + encodeURIComponent(folder))
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
const fileListContainer = document.getElementById("fileList");
|
|
||||||
fileListContainer.innerHTML = "";
|
|
||||||
if (data.files && data.files.length > 0) {
|
|
||||||
const table = document.createElement("table");
|
|
||||||
table.classList.add("table");
|
|
||||||
const thead = document.createElement("thead");
|
|
||||||
const headerRow = document.createElement("tr");
|
|
||||||
// Add select-all checkbox in header.
|
|
||||||
const selectTh = document.createElement("th");
|
|
||||||
const selectAll = document.createElement("input");
|
|
||||||
selectAll.type = "checkbox";
|
|
||||||
selectAll.id = "selectAllFiles";
|
|
||||||
selectAll.addEventListener("change", function () {
|
|
||||||
const checkboxes = document.querySelectorAll(".file-checkbox");
|
|
||||||
checkboxes.forEach(chk => chk.checked = this.checked);
|
|
||||||
updateDeleteSelectedVisibility();
|
|
||||||
});
|
|
||||||
selectTh.appendChild(selectAll);
|
|
||||||
headerRow.appendChild(selectTh);
|
|
||||||
["Name", "Modified", "Uploaded", "Size", "Uploader", "Actions"].forEach(headerText => {
|
|
||||||
const th = document.createElement("th");
|
|
||||||
th.textContent = headerText;
|
|
||||||
headerRow.appendChild(th);
|
|
||||||
});
|
|
||||||
thead.appendChild(headerRow);
|
|
||||||
table.appendChild(thead);
|
|
||||||
const tbody = document.createElement("tbody");
|
|
||||||
const folderPath = (folder === "root") ? "uploads/" : "uploads/" + encodeURIComponent(folder) + "/";
|
|
||||||
data.files.forEach(file => {
|
|
||||||
const row = document.createElement("tr");
|
|
||||||
const checkboxTd = document.createElement("td");
|
|
||||||
const checkbox = document.createElement("input");
|
|
||||||
checkbox.type = "checkbox";
|
|
||||||
checkbox.className = "file-checkbox";
|
|
||||||
checkbox.value = file.name;
|
|
||||||
checkbox.addEventListener("change", updateDeleteSelectedVisibility);
|
|
||||||
checkboxTd.appendChild(checkbox);
|
|
||||||
row.appendChild(checkboxTd);
|
|
||||||
const nameTd = document.createElement("td");
|
|
||||||
nameTd.textContent = file.name;
|
|
||||||
row.appendChild(nameTd);
|
|
||||||
const modifiedTd = document.createElement("td");
|
|
||||||
modifiedTd.textContent = file.modified;
|
|
||||||
row.appendChild(modifiedTd);
|
|
||||||
const uploadedTd = document.createElement("td");
|
|
||||||
uploadedTd.textContent = file.uploaded;
|
|
||||||
row.appendChild(uploadedTd);
|
|
||||||
const sizeTd = document.createElement("td");
|
|
||||||
sizeTd.textContent = file.size;
|
|
||||||
row.appendChild(sizeTd);
|
|
||||||
const uploaderTd = document.createElement("td");
|
|
||||||
uploaderTd.textContent = file.uploader;
|
|
||||||
row.appendChild(uploaderTd);
|
|
||||||
const actionsTd = document.createElement("td");
|
|
||||||
actionsTd.className = "actions-cell"; // prevent wrapping
|
|
||||||
const downloadButton = document.createElement("a");
|
|
||||||
downloadButton.className = "btn btn-sm btn-success";
|
|
||||||
downloadButton.href = folderPath + encodeURIComponent(file.name);
|
|
||||||
downloadButton.download = file.name;
|
|
||||||
downloadButton.textContent = "Download";
|
|
||||||
actionsTd.appendChild(downloadButton);
|
|
||||||
if (canEditFile(file.name)) {
|
|
||||||
const editButton = document.createElement("button");
|
|
||||||
editButton.className = "btn btn-sm btn-primary ml-2";
|
|
||||||
editButton.textContent = "Edit";
|
|
||||||
editButton.addEventListener("click", function () {
|
|
||||||
editFile(file.name, currentFolder);
|
|
||||||
});
|
|
||||||
actionsTd.appendChild(editButton);
|
|
||||||
}
|
|
||||||
row.appendChild(actionsTd);
|
|
||||||
tbody.appendChild(row);
|
|
||||||
});
|
|
||||||
table.appendChild(tbody);
|
|
||||||
fileListContainer.appendChild(table);
|
|
||||||
updateDeleteSelectedVisibility();
|
|
||||||
} else {
|
|
||||||
fileListContainer.textContent = "No files found.";
|
|
||||||
document.getElementById("deleteSelectedBtn").style.display = "none";
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error loading file list:", error));
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateDeleteSelectedVisibility() {
|
|
||||||
const checkboxes = document.querySelectorAll(".file-checkbox");
|
|
||||||
const deleteBtn = document.getElementById("deleteSelectedBtn");
|
|
||||||
if (checkboxes.length > 0) {
|
|
||||||
deleteBtn.style.display = "inline-block";
|
|
||||||
let anyChecked = false;
|
|
||||||
checkboxes.forEach(chk => {
|
|
||||||
if (chk.checked) anyChecked = true;
|
|
||||||
});
|
|
||||||
deleteBtn.disabled = !anyChecked;
|
|
||||||
} else {
|
|
||||||
deleteBtn.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDeleteSelected(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopImmediatePropagation();
|
|
||||||
const checkboxes = document.querySelectorAll(".file-checkbox:checked");
|
|
||||||
if (checkboxes.length === 0) {
|
|
||||||
alert("No files selected.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!confirm("Are you sure you want to delete the selected files?")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const filesToDelete = Array.from(checkboxes).map(chk => chk.value);
|
|
||||||
fetch("deleteFiles.php", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ folder: currentFolder, files: filesToDelete })
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
alert("Selected files deleted successfully!");
|
|
||||||
loadFileList(currentFolder);
|
|
||||||
} else {
|
|
||||||
alert("Error: " + (data.error || "Could not delete files"));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error deleting files:", error));
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteSelectedBtn = document.getElementById("deleteSelectedBtn");
|
|
||||||
deleteSelectedBtn.replaceWith(deleteSelectedBtn.cloneNode(true));
|
|
||||||
document.getElementById("deleteSelectedBtn").addEventListener("click", handleDeleteSelected);
|
|
||||||
|
|
||||||
window.editFile = function (fileName, folder) {
|
|
||||||
console.log("Edit button clicked for:", fileName);
|
|
||||||
let existingEditor = document.getElementById("editorContainer");
|
|
||||||
if (existingEditor) { existingEditor.remove(); }
|
|
||||||
const folderUsed = folder || currentFolder || "root";
|
|
||||||
const folderPath = (folderUsed === "root") ? "uploads/" : "uploads/" + encodeURIComponent(folderUsed) + "/";
|
|
||||||
fetch(folderPath + encodeURIComponent(fileName) + "?t=" + new Date().getTime())
|
|
||||||
.then(response => {
|
|
||||||
if (!response.ok) { throw new Error("HTTP error! Status: " + response.status); }
|
|
||||||
return response.text();
|
|
||||||
})
|
|
||||||
.then(content => {
|
|
||||||
const modal = document.createElement("div");
|
|
||||||
modal.id = "editorContainer";
|
|
||||||
modal.classList.add("modal", "editor-modal");
|
|
||||||
modal.innerHTML = `
|
|
||||||
<h3>Editing: ${fileName}</h3>
|
|
||||||
<textarea id="fileEditor" style="width:100%; height:80%; resize:none;">${content}</textarea>
|
|
||||||
<div style="margin-top:10px; text-align:right;">
|
|
||||||
<button onclick="saveFile('${fileName}', '${folderUsed}')" class="btn btn-primary">Save</button>
|
|
||||||
<button onclick="document.getElementById('editorContainer').remove()" class="btn btn-secondary">Close</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
document.body.appendChild(modal);
|
|
||||||
modal.style.display = "block";
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error loading file:", error));
|
|
||||||
};
|
|
||||||
|
|
||||||
window.saveFile = function (fileName, folder) {
|
|
||||||
const editor = document.getElementById("fileEditor");
|
|
||||||
if (!editor) {
|
|
||||||
console.error("Editor not found!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const folderUsed = folder || currentFolder || "root";
|
|
||||||
const fileDataObj = {
|
|
||||||
fileName: fileName,
|
|
||||||
content: editor.value,
|
|
||||||
folder: folderUsed
|
|
||||||
};
|
|
||||||
fetch("saveFile.php", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(fileDataObj)
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => {
|
|
||||||
alert(result.success || result.error);
|
|
||||||
document.getElementById("editorContainer")?.remove();
|
|
||||||
loadFileList(folderUsed);
|
|
||||||
})
|
|
||||||
.catch(error => console.error("Error saving file:", error));
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- Upload Form Handling ---
|
|
||||||
const fileInput = document.getElementById("file");
|
|
||||||
const progressContainer = document.getElementById("uploadProgressContainer");
|
|
||||||
const uploadForm = document.getElementById("uploadFileForm");
|
|
||||||
|
|
||||||
fileInput.addEventListener("change", function () {
|
|
||||||
progressContainer.innerHTML = "";
|
|
||||||
const files = fileInput.files;
|
|
||||||
if (files.length > 0) {
|
|
||||||
const list = document.createElement("ul");
|
|
||||||
list.style.listStyle = "none";
|
|
||||||
list.style.padding = "0";
|
|
||||||
Array.from(files).forEach((file, index) => {
|
|
||||||
const listItem = document.createElement("li");
|
|
||||||
listItem.style.paddingTop = "20px";
|
|
||||||
listItem.style.marginBottom = "10px";
|
|
||||||
listItem.style.display = "flex";
|
|
||||||
listItem.style.alignItems = "center";
|
|
||||||
listItem.style.flexWrap = "wrap";
|
|
||||||
const previewContainer = document.createElement("div");
|
|
||||||
previewContainer.className = "file-preview";
|
|
||||||
displayFilePreview(file, previewContainer);
|
|
||||||
const fileNameDiv = document.createElement("div");
|
|
||||||
fileNameDiv.textContent = file.name;
|
|
||||||
fileNameDiv.style.flexGrow = "1";
|
|
||||||
fileNameDiv.style.marginLeft = "10px";
|
|
||||||
fileNameDiv.style.wordBreak = "break-word";
|
|
||||||
const progressDiv = document.createElement("div");
|
|
||||||
progressDiv.classList.add("progress");
|
|
||||||
progressDiv.style.flex = "0 0 250px";
|
|
||||||
progressDiv.style.marginLeft = "5px";
|
|
||||||
const progressBar = document.createElement("div");
|
|
||||||
progressBar.classList.add("progress-bar");
|
|
||||||
progressBar.style.width = "0%";
|
|
||||||
progressBar.innerText = "0%";
|
|
||||||
progressDiv.appendChild(progressBar);
|
|
||||||
listItem.appendChild(previewContainer);
|
|
||||||
listItem.appendChild(fileNameDiv);
|
|
||||||
listItem.appendChild(progressDiv);
|
|
||||||
listItem.progressBar = progressBar;
|
|
||||||
listItem.startTime = Date.now();
|
|
||||||
list.appendChild(listItem);
|
|
||||||
});
|
|
||||||
progressContainer.appendChild(list);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
uploadForm.addEventListener("submit", function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const files = fileInput.files;
|
|
||||||
if (files.length === 0) {
|
|
||||||
alert("No files selected.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const folderToUse = currentFolder || "root";
|
|
||||||
const listItems = progressContainer.querySelectorAll("li");
|
|
||||||
let finishedCount = 0;
|
|
||||||
Array.from(files).forEach((file, index) => {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("file[]", file);
|
|
||||||
formData.append("folder", folderToUse);
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
let currentPercent = 0;
|
|
||||||
xhr.upload.addEventListener("progress", function (e) {
|
|
||||||
if (e.lengthComputable) {
|
|
||||||
currentPercent = Math.round((e.loaded / e.total) * 100);
|
|
||||||
const elapsedTime = (Date.now() - listItems[index].startTime) / 1000;
|
|
||||||
let speedText = "";
|
|
||||||
if (elapsedTime > 0) {
|
|
||||||
const speed = e.loaded / elapsedTime;
|
|
||||||
if (speed < 1024) speedText = speed.toFixed(0) + " B/s";
|
|
||||||
else if (speed < 1048576) speedText = (speed / 1024).toFixed(1) + " KB/s";
|
|
||||||
else speedText = (speed / 1048576).toFixed(1) + " MB/s";
|
|
||||||
}
|
|
||||||
listItems[index].progressBar.style.width = currentPercent + "%";
|
|
||||||
listItems[index].progressBar.innerText = currentPercent + "% (" + speedText + ")";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
xhr.addEventListener("load", function () {
|
|
||||||
if (currentPercent >= 100) {
|
|
||||||
listItems[index].progressBar.innerText = "Done";
|
|
||||||
}
|
|
||||||
finishedCount++;
|
|
||||||
console.log("Upload response for file", file.name, xhr.responseText);
|
|
||||||
if (finishedCount === files.length) {
|
|
||||||
loadFileList(folderToUse);
|
|
||||||
fileInput.value = "";
|
|
||||||
setTimeout(() => { progressContainer.innerHTML = ""; }, 5000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
xhr.addEventListener("error", function () {
|
|
||||||
listItems[index].progressBar.innerText = "Error";
|
|
||||||
});
|
|
||||||
xhr.open("POST", "upload.php", true);
|
|
||||||
xhr.send(formData);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
124
upload.js
124
upload.js
@@ -1,128 +1,109 @@
|
|||||||
document.addEventListener("DOMContentLoaded", function() {
|
// upload.js
|
||||||
|
import { displayFilePreview } from './utils.js';
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
const fileInput = document.getElementById("file");
|
const fileInput = document.getElementById("file");
|
||||||
const progressContainer = document.getElementById("uploadProgressContainer");
|
const progressContainer = document.getElementById("uploadProgressContainer");
|
||||||
const uploadForm = document.getElementById("uploadFileForm");
|
const uploadForm = document.getElementById("uploadFileForm");
|
||||||
|
|
||||||
// When files are selected, display a list with preview, file name, and a progress bar.
|
function updateUploadProgress(e, listItem) {
|
||||||
fileInput.addEventListener("change", function() {
|
if (e.lengthComputable) {
|
||||||
progressContainer.innerHTML = ""; // Clear previous entries
|
const currentPercent = Math.round((e.loaded / e.total) * 100);
|
||||||
|
const elapsedTime = (Date.now() - listItem.startTime) / 1000;
|
||||||
|
let speedText = "";
|
||||||
|
if (elapsedTime > 0) {
|
||||||
|
const speed = e.loaded / elapsedTime;
|
||||||
|
if (speed < 1024) {
|
||||||
|
speedText = speed.toFixed(0) + " B/s";
|
||||||
|
} else if (speed < 1048576) {
|
||||||
|
speedText = (speed / 1024).toFixed(1) + " KB/s";
|
||||||
|
} else {
|
||||||
|
speedText = (speed / 1048576).toFixed(1) + " MB/s";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listItem.progressBar.style.width = currentPercent + "%";
|
||||||
|
listItem.progressBar.innerText = currentPercent + "% (" + speedText + ")";
|
||||||
|
return currentPercent;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInput.addEventListener("change", function () {
|
||||||
|
progressContainer.innerHTML = "";
|
||||||
const files = fileInput.files;
|
const files = fileInput.files;
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
const list = document.createElement("ul");
|
const list = document.createElement("ul");
|
||||||
list.style.listStyle = "none";
|
list.style.listStyle = "none";
|
||||||
list.style.padding = "0";
|
list.style.padding = "0";
|
||||||
|
Array.from(files).forEach((file) => {
|
||||||
Array.from(files).forEach((file, index) => {
|
|
||||||
const listItem = document.createElement("li");
|
const listItem = document.createElement("li");
|
||||||
listItem.style.paddingTop = "20px";
|
listItem.style.paddingTop = "20px";
|
||||||
listItem.style.marginBottom = "10px";
|
listItem.style.marginBottom = "10px";
|
||||||
listItem.style.display = "flex";
|
listItem.style.display = "flex";
|
||||||
listItem.style.alignItems = "center";
|
listItem.style.alignItems = "center";
|
||||||
listItem.style.flexWrap = "wrap";
|
listItem.style.flexWrap = "wrap";
|
||||||
|
|
||||||
// Create preview container (32x32)
|
|
||||||
const previewContainer = document.createElement("div");
|
const previewContainer = document.createElement("div");
|
||||||
previewContainer.className = "file-preview";
|
previewContainer.className = "file-preview";
|
||||||
if (file.type.startsWith("image/")) {
|
displayFilePreview(file, previewContainer);
|
||||||
const img = document.createElement("img");
|
|
||||||
img.style.width = "32px";
|
|
||||||
img.style.height = "32px";
|
|
||||||
img.style.objectFit = "cover";
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = function(e) {
|
|
||||||
img.src = e.target.result;
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
previewContainer.appendChild(img);
|
|
||||||
} else {
|
|
||||||
const icon = document.createElement("i");
|
|
||||||
icon.className = "material-icons";
|
|
||||||
icon.textContent = "insert_drive_file";
|
|
||||||
icon.style.fontSize = "32px";
|
|
||||||
previewContainer.appendChild(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
// File name container – allow wrapping
|
|
||||||
const fileNameDiv = document.createElement("div");
|
const fileNameDiv = document.createElement("div");
|
||||||
fileNameDiv.textContent = file.name;
|
fileNameDiv.textContent = file.name;
|
||||||
fileNameDiv.style.flexGrow = "1";
|
fileNameDiv.style.flexGrow = "1";
|
||||||
fileNameDiv.style.marginLeft = "10px";
|
fileNameDiv.style.marginLeft = "10px";
|
||||||
fileNameDiv.style.wordBreak = "break-word";
|
fileNameDiv.style.wordBreak = "break-word";
|
||||||
|
|
||||||
// Progress bar container (fixed width)
|
|
||||||
const progressDiv = document.createElement("div");
|
const progressDiv = document.createElement("div");
|
||||||
progressDiv.classList.add("progress");
|
progressDiv.classList.add("progress");
|
||||||
progressDiv.style.flex = "0 0 250px";
|
progressDiv.style.flex = "0 0 250px";
|
||||||
progressDiv.style.marginLeft = "5px";
|
progressDiv.style.marginLeft = "5px";
|
||||||
|
|
||||||
const progressBar = document.createElement("div");
|
const progressBar = document.createElement("div");
|
||||||
progressBar.classList.add("progress-bar");
|
progressBar.classList.add("progress-bar");
|
||||||
progressBar.style.width = "0%";
|
progressBar.style.width = "0%";
|
||||||
progressBar.innerText = "0%";
|
progressBar.innerText = "0%";
|
||||||
|
|
||||||
progressDiv.appendChild(progressBar);
|
progressDiv.appendChild(progressBar);
|
||||||
|
|
||||||
// Assemble the list item
|
|
||||||
listItem.appendChild(previewContainer);
|
listItem.appendChild(previewContainer);
|
||||||
listItem.appendChild(fileNameDiv);
|
listItem.appendChild(fileNameDiv);
|
||||||
listItem.appendChild(progressDiv);
|
listItem.appendChild(progressDiv);
|
||||||
|
|
||||||
// Save reference for updates and record start time for speed calculation.
|
|
||||||
listItem.progressBar = progressBar;
|
listItem.progressBar = progressBar;
|
||||||
listItem.startTime = Date.now();
|
listItem.startTime = Date.now();
|
||||||
|
|
||||||
list.appendChild(listItem);
|
list.appendChild(listItem);
|
||||||
});
|
});
|
||||||
|
|
||||||
progressContainer.appendChild(list);
|
progressContainer.appendChild(list);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// On form submit, upload each file individually and update its corresponding progress bar.
|
uploadForm.addEventListener("submit", function (e) {
|
||||||
uploadForm.addEventListener("submit", function(e) {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const files = fileInput.files;
|
const files = fileInput.files;
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
alert("No files selected.");
|
alert("No files selected.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const listItems = progressContainer.querySelectorAll("li");
|
const listItems = progressContainer.querySelectorAll("li");
|
||||||
let finishedCount = 0;
|
let finishedCount = 0;
|
||||||
|
|
||||||
Array.from(files).forEach((file, index) => {
|
Array.from(files).forEach((file, index) => {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
// Append the file
|
|
||||||
formData.append("file[]", file);
|
formData.append("file[]", file);
|
||||||
// Append the current folder value from the hidden input
|
const folderElem = document.getElementById("folderSelect");
|
||||||
formData.append("folder", document.getElementById("uploadFolder").value);
|
if (folderElem) {
|
||||||
|
formData.append("folder", folderElem.value);
|
||||||
|
} else {
|
||||||
|
console.error("Folder selection element not found!");
|
||||||
|
}
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
let currentPercent = 0;
|
let currentPercent = 0;
|
||||||
|
xhr.upload.addEventListener("progress", function (e) {
|
||||||
// Update progress events.
|
currentPercent = updateUploadProgress(e, listItems[index]);
|
||||||
xhr.upload.addEventListener("progress", function(e) {
|
|
||||||
if (e.lengthComputable) {
|
|
||||||
currentPercent = Math.round((e.loaded / e.total) * 100);
|
|
||||||
const elapsedTime = (Date.now() - listItems[index].startTime) / 1000; // seconds
|
|
||||||
let speedText = "";
|
|
||||||
if (elapsedTime > 0) {
|
|
||||||
const speed = e.loaded / elapsedTime; // bytes per second
|
|
||||||
if (speed < 1024) {
|
|
||||||
speedText = speed.toFixed(0) + " B/s";
|
|
||||||
} else if (speed < 1048576) {
|
|
||||||
speedText = (speed / 1024).toFixed(1) + " KB/s";
|
|
||||||
} else {
|
|
||||||
speedText = (speed / 1048576).toFixed(1) + " MB/s";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
listItems[index].progressBar.style.width = currentPercent + "%";
|
|
||||||
listItems[index].progressBar.innerText = currentPercent + "% (" + speedText + ")";
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
xhr.addEventListener("load", function () {
|
||||||
// Use the load event for final update.
|
|
||||||
xhr.addEventListener("load", function() {
|
|
||||||
if (currentPercent >= 100) {
|
if (currentPercent >= 100) {
|
||||||
listItems[index].progressBar.innerText = "Done";
|
listItems[index].progressBar.innerText = "Done";
|
||||||
}
|
}
|
||||||
@@ -132,18 +113,15 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||||||
if (typeof loadFileList === "function") {
|
if (typeof loadFileList === "function") {
|
||||||
loadFileList();
|
loadFileList();
|
||||||
}
|
}
|
||||||
// Reset file input so "No files selected" appears
|
|
||||||
fileInput.value = "";
|
fileInput.value = "";
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
progressContainer.innerHTML = "";
|
progressContainer.innerHTML = "";
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
xhr.addEventListener("error", function () {
|
||||||
xhr.addEventListener("error", function() {
|
|
||||||
listItems[index].progressBar.innerText = "Error";
|
listItems[index].progressBar.innerText = "Error";
|
||||||
});
|
});
|
||||||
|
|
||||||
xhr.open("POST", "upload.php", true);
|
xhr.open("POST", "upload.php", true);
|
||||||
xhr.send(formData);
|
xhr.send(formData);
|
||||||
});
|
});
|
||||||
|
|||||||
704
utils.js
Normal file
704
utils.js
Normal file
@@ -0,0 +1,704 @@
|
|||||||
|
// =======================
|
||||||
|
// Utility Functions
|
||||||
|
// =======================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an AJAX request using the Fetch API.
|
||||||
|
* @param {string} url - The endpoint URL.
|
||||||
|
* @param {string} [method="GET"] - The HTTP method.
|
||||||
|
* @param {object|null} [data=null] - The payload to send (for POST/PUT).
|
||||||
|
* @returns {Promise} Resolves with JSON (or text) response or rejects with an error.
|
||||||
|
*/
|
||||||
|
export function sendRequest(url, method = "GET", data = null) {
|
||||||
|
console.log("Sending request to:", url, "with method:", method);
|
||||||
|
const options = { method, headers: { "Content-Type": "application/json" } };
|
||||||
|
if (data) {
|
||||||
|
options.body = JSON.stringify(data);
|
||||||
|
}
|
||||||
|
return fetch(url, options)
|
||||||
|
.then(response => {
|
||||||
|
console.log("Response status:", response.status);
|
||||||
|
if (!response.ok) {
|
||||||
|
return response.text().then(text => {
|
||||||
|
throw new Error(`HTTP error ${response.status}: ${text}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json().catch(() => {
|
||||||
|
console.warn("Response is not JSON, returning as text");
|
||||||
|
return response.text();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the display of an element by its ID.
|
||||||
|
* @param {string} elementId - The element’s ID.
|
||||||
|
* @param {boolean} shouldShow - True to display the element, false to hide.
|
||||||
|
*/
|
||||||
|
export function toggleVisibility(elementId, shouldShow) {
|
||||||
|
const element = document.getElementById(elementId);
|
||||||
|
if (element) {
|
||||||
|
element.style.display = shouldShow ? "block" : "none";
|
||||||
|
} else {
|
||||||
|
console.error(`Element with id "${elementId}" not found.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose utilities to the global scope.
|
||||||
|
window.sendRequest = sendRequest;
|
||||||
|
window.toggleVisibility = toggleVisibility;
|
||||||
|
|
||||||
|
// =======================
|
||||||
|
// Application Code
|
||||||
|
// =======================
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
let currentFolder = "root";
|
||||||
|
let setupMode = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a file is editable based on its extension.
|
||||||
|
* @param {string} fileName
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function canEditFile(fileName) {
|
||||||
|
const allowedExtensions = ["txt", "html", "htm", "php", "css", "js", "json", "xml", "md", "py"];
|
||||||
|
const parts = fileName.split('.');
|
||||||
|
if (parts.length < 2) return false;
|
||||||
|
const ext = parts.pop().toLowerCase();
|
||||||
|
return allowedExtensions.includes(ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a file preview (either an image or an icon) in a container.
|
||||||
|
* @param {File} file - The file to preview.
|
||||||
|
* @param {HTMLElement} container - The container to append the preview.
|
||||||
|
*/
|
||||||
|
export function displayFilePreview(file, container) {
|
||||||
|
if (file.type.startsWith("image/")) {
|
||||||
|
const img = document.createElement("img");
|
||||||
|
img.style.width = "32px";
|
||||||
|
img.style.height = "32px";
|
||||||
|
img.style.objectFit = "cover";
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function (e) {
|
||||||
|
img.src = e.target.result;
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
container.appendChild(img);
|
||||||
|
} else {
|
||||||
|
const icon = document.createElement("i");
|
||||||
|
icon.className = "material-icons";
|
||||||
|
icon.style.fontSize = "32px";
|
||||||
|
icon.textContent = "insert_drive_file";
|
||||||
|
container.appendChild(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================
|
||||||
|
// DOMContentLoaded
|
||||||
|
// =======================
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
|
||||||
|
checkAuthentication();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the UI based on authentication and setup data.
|
||||||
|
* @param {object} data
|
||||||
|
*/
|
||||||
|
function updateUI(data) {
|
||||||
|
console.log("Auth data:", data);
|
||||||
|
if (data.setup) {
|
||||||
|
setupMode = true;
|
||||||
|
toggleVisibility("loginForm", false);
|
||||||
|
document.getElementById("mainOperations").style.display = "none";
|
||||||
|
document.getElementById("fileListContainer").style.display = "none";
|
||||||
|
document.querySelector(".header-buttons").style.visibility = "hidden";
|
||||||
|
document.getElementById("addUserModal").style.display = "block";
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
setupMode = false;
|
||||||
|
}
|
||||||
|
if (data.authenticated) {
|
||||||
|
toggleVisibility("loginForm", false);
|
||||||
|
document.getElementById("mainOperations").style.display = "block";
|
||||||
|
document.getElementById("fileListContainer").style.display = "block";
|
||||||
|
document.querySelector(".header-buttons").style.visibility = "visible";
|
||||||
|
if (data.isAdmin) {
|
||||||
|
document.getElementById("logoutBtn").style.display = "block";
|
||||||
|
document.getElementById("addUserBtn").style.display = "block";
|
||||||
|
document.getElementById("removeUserBtn").style.display = "block";
|
||||||
|
} else {
|
||||||
|
document.getElementById("logoutBtn").style.display = "block";
|
||||||
|
document.getElementById("addUserBtn").style.display = "none";
|
||||||
|
document.getElementById("removeUserBtn").style.display = "none";
|
||||||
|
}
|
||||||
|
loadFolderList();
|
||||||
|
} else {
|
||||||
|
// Show login form if not authenticated.
|
||||||
|
toggleVisibility("loginForm", true);
|
||||||
|
document.getElementById("mainOperations").style.display = "none";
|
||||||
|
document.getElementById("fileListContainer").style.display = "none";
|
||||||
|
document.querySelector(".header-buttons").style.visibility = "hidden";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the user is authenticated.
|
||||||
|
*/
|
||||||
|
function checkAuthentication() {
|
||||||
|
sendRequest("checkAuth.php")
|
||||||
|
.then(updateUI)
|
||||||
|
.catch(error => console.error("Error checking authentication:", error));
|
||||||
|
}
|
||||||
|
window.checkAuthentication = checkAuthentication;
|
||||||
|
|
||||||
|
// -----------------------
|
||||||
|
// Authentication Form
|
||||||
|
// -----------------------
|
||||||
|
document.getElementById("authForm").addEventListener("submit", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const formData = {
|
||||||
|
username: document.getElementById("loginUsername").value.trim(),
|
||||||
|
password: document.getElementById("loginPassword").value.trim()
|
||||||
|
};
|
||||||
|
sendRequest("auth.php", "POST", formData)
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
updateUI({ authenticated: true, isAdmin: data.isAdmin });
|
||||||
|
} else {
|
||||||
|
alert("Login failed: " + (data.error || "Unknown error"));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error logging in:", error));
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("logoutBtn").addEventListener("click", function () {
|
||||||
|
fetch("logout.php", { method: "POST" })
|
||||||
|
.then(() => window.location.reload(true))
|
||||||
|
.catch(error => console.error("Logout error:", error));
|
||||||
|
});
|
||||||
|
|
||||||
|
// -----------------------
|
||||||
|
// Add User Functionality
|
||||||
|
// -----------------------
|
||||||
|
document.getElementById("addUserBtn").addEventListener("click", function () {
|
||||||
|
resetUserForm();
|
||||||
|
document.getElementById("addUserModal").style.display = "block";
|
||||||
|
});
|
||||||
|
document.getElementById("saveUserBtn").addEventListener("click", function () {
|
||||||
|
const newUsername = document.getElementById("newUsername").value.trim();
|
||||||
|
const newPassword = document.getElementById("newPassword").value.trim();
|
||||||
|
const isAdmin = document.getElementById("isAdmin").checked;
|
||||||
|
if (!newUsername || !newPassword) {
|
||||||
|
alert("Username and password are required!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let url = "addUser.php";
|
||||||
|
if (setupMode) {
|
||||||
|
url += "?setup=1";
|
||||||
|
}
|
||||||
|
fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ username: newUsername, password: newPassword, isAdmin })
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
alert("User added successfully!");
|
||||||
|
closeAddUserModal();
|
||||||
|
checkAuthentication();
|
||||||
|
} else {
|
||||||
|
alert("Error: " + (data.error || "Could not add user"));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error adding user:", error));
|
||||||
|
});
|
||||||
|
document.getElementById("cancelUserBtn").addEventListener("click", function () {
|
||||||
|
closeAddUserModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -----------------------
|
||||||
|
// Remove User Functionality
|
||||||
|
// -----------------------
|
||||||
|
document.getElementById("removeUserBtn").addEventListener("click", function () {
|
||||||
|
loadUserList();
|
||||||
|
document.getElementById("removeUserModal").style.display = "block";
|
||||||
|
});
|
||||||
|
document.getElementById("deleteUserBtn").addEventListener("click", function () {
|
||||||
|
const selectElem = document.getElementById("removeUsernameSelect");
|
||||||
|
const usernameToRemove = selectElem.value;
|
||||||
|
if (!usernameToRemove) {
|
||||||
|
alert("Please select a user to remove.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!confirm("Are you sure you want to delete user " + usernameToRemove + "?")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetch("removeUser.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ username: usernameToRemove })
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
alert("User removed successfully!");
|
||||||
|
closeRemoveUserModal();
|
||||||
|
loadUserList();
|
||||||
|
} else {
|
||||||
|
alert("Error: " + (data.error || "Could not remove user"));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error removing user:", error));
|
||||||
|
});
|
||||||
|
document.getElementById("cancelRemoveUserBtn").addEventListener("click", function () {
|
||||||
|
closeRemoveUserModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
function closeAddUserModal() {
|
||||||
|
document.getElementById("addUserModal").style.display = "none";
|
||||||
|
resetUserForm();
|
||||||
|
}
|
||||||
|
function resetUserForm() {
|
||||||
|
document.getElementById("newUsername").value = "";
|
||||||
|
document.getElementById("newPassword").value = "";
|
||||||
|
}
|
||||||
|
function closeRemoveUserModal() {
|
||||||
|
document.getElementById("removeUserModal").style.display = "none";
|
||||||
|
document.getElementById("removeUsernameSelect").innerHTML = "";
|
||||||
|
}
|
||||||
|
function loadUserList() {
|
||||||
|
fetch("getUsers.php")
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const users = Array.isArray(data) ? data : (data.users || []);
|
||||||
|
if (!users || !Array.isArray(users)) {
|
||||||
|
console.error("Invalid users data:", data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectElem = document.getElementById("removeUsernameSelect");
|
||||||
|
selectElem.innerHTML = "";
|
||||||
|
users.forEach(user => {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = user.username;
|
||||||
|
option.textContent = user.username;
|
||||||
|
selectElem.appendChild(option);
|
||||||
|
});
|
||||||
|
if (selectElem.options.length === 0) {
|
||||||
|
alert("No other users found to remove.");
|
||||||
|
closeRemoveUserModal();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error loading user list:", error));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------
|
||||||
|
// Folder Management
|
||||||
|
// -----------------------
|
||||||
|
function loadFolderList(selectedFolder) {
|
||||||
|
const folderSelect = document.getElementById("folderSelect");
|
||||||
|
folderSelect.innerHTML = "";
|
||||||
|
const rootOption = document.createElement("option");
|
||||||
|
rootOption.value = "root";
|
||||||
|
rootOption.textContent = "(Root)";
|
||||||
|
folderSelect.appendChild(rootOption);
|
||||||
|
fetch("getFolderList.php")
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(folders => {
|
||||||
|
folders.forEach(function (folder) {
|
||||||
|
let option = document.createElement("option");
|
||||||
|
option.value = folder;
|
||||||
|
option.textContent = folder;
|
||||||
|
folderSelect.appendChild(option);
|
||||||
|
});
|
||||||
|
if (selectedFolder && [...folderSelect.options].some(opt => opt.value === selectedFolder)) {
|
||||||
|
folderSelect.value = selectedFolder;
|
||||||
|
} else {
|
||||||
|
folderSelect.value = "root";
|
||||||
|
}
|
||||||
|
currentFolder = folderSelect.value;
|
||||||
|
document.getElementById("fileListTitle").textContent =
|
||||||
|
currentFolder === "root" ? "Files in (Root)" : "Files in (" + currentFolder + ")";
|
||||||
|
loadFileList(currentFolder);
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error loading folder list:", error));
|
||||||
|
}
|
||||||
|
document.getElementById("folderSelect").addEventListener("change", function () {
|
||||||
|
currentFolder = this.value;
|
||||||
|
document.getElementById("fileListTitle").textContent =
|
||||||
|
currentFolder === "root" ? "Files in (Root)" : "Files in (" + currentFolder + ")";
|
||||||
|
loadFileList(currentFolder);
|
||||||
|
});
|
||||||
|
document.getElementById("createFolderBtn").addEventListener("click", function () {
|
||||||
|
let folderName = prompt("Enter folder name:");
|
||||||
|
if (folderName) {
|
||||||
|
fetch("createFolder.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ folder: folderName })
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
alert("Folder created successfully!");
|
||||||
|
loadFolderList(folderName);
|
||||||
|
} else {
|
||||||
|
alert("Error: " + (data.error || "Could not create folder"));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error creating folder:", error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.getElementById("renameFolderBtn").addEventListener("click", function () {
|
||||||
|
const folderSelect = document.getElementById("folderSelect");
|
||||||
|
const selectedFolder = folderSelect.value;
|
||||||
|
if (!selectedFolder || selectedFolder === "root") {
|
||||||
|
alert("Please select a valid folder to rename.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let newFolderName = prompt("Enter new folder name for '" + selectedFolder + "':", selectedFolder);
|
||||||
|
if (newFolderName && newFolderName !== selectedFolder) {
|
||||||
|
fetch("renameFolder.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ oldFolder: selectedFolder, newFolder: newFolderName })
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
alert("Folder renamed successfully!");
|
||||||
|
loadFolderList(newFolderName);
|
||||||
|
} else {
|
||||||
|
alert("Error: " + (data.error || "Could not rename folder"));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error renaming folder:", error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.getElementById("deleteFolderBtn").addEventListener("click", function () {
|
||||||
|
const folderSelect = document.getElementById("folderSelect");
|
||||||
|
const selectedFolder = folderSelect.value;
|
||||||
|
if (!selectedFolder || selectedFolder === "root") {
|
||||||
|
alert("Please select a valid folder to delete.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (confirm("Are you sure you want to delete folder " + selectedFolder + "?")) {
|
||||||
|
fetch("deleteFolder.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ folder: selectedFolder })
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
alert("Folder deleted successfully!");
|
||||||
|
loadFolderList("root");
|
||||||
|
} else {
|
||||||
|
alert("Error: " + (data.error || "Could not delete folder"));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error deleting folder:", error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// -----------------------
|
||||||
|
// File List Management
|
||||||
|
// -----------------------
|
||||||
|
function loadFileList(folderParam) {
|
||||||
|
const folder = folderParam || currentFolder || "root";
|
||||||
|
fetch("getFileList.php?folder=" + encodeURIComponent(folder))
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const fileListContainer = document.getElementById("fileList");
|
||||||
|
fileListContainer.innerHTML = "";
|
||||||
|
if (data.files && data.files.length > 0) {
|
||||||
|
const table = document.createElement("table");
|
||||||
|
table.classList.add("table");
|
||||||
|
const thead = document.createElement("thead");
|
||||||
|
const headerRow = document.createElement("tr");
|
||||||
|
// Add select-all checkbox in header.
|
||||||
|
const selectTh = document.createElement("th");
|
||||||
|
const selectAll = document.createElement("input");
|
||||||
|
selectAll.type = "checkbox";
|
||||||
|
selectAll.id = "selectAllFiles";
|
||||||
|
selectAll.addEventListener("change", function () {
|
||||||
|
const checkboxes = document.querySelectorAll(".file-checkbox");
|
||||||
|
checkboxes.forEach(chk => chk.checked = this.checked);
|
||||||
|
updateDeleteSelectedVisibility();
|
||||||
|
});
|
||||||
|
selectTh.appendChild(selectAll);
|
||||||
|
headerRow.appendChild(selectTh);
|
||||||
|
["Name", "Modified", "Uploaded", "Size", "Uploader", "Actions"].forEach(headerText => {
|
||||||
|
const th = document.createElement("th");
|
||||||
|
th.textContent = headerText;
|
||||||
|
headerRow.appendChild(th);
|
||||||
|
});
|
||||||
|
thead.appendChild(headerRow);
|
||||||
|
table.appendChild(thead);
|
||||||
|
const tbody = document.createElement("tbody");
|
||||||
|
const folderPath = (folder === "root") ? "uploads/" : "uploads/" + encodeURIComponent(folder) + "/";
|
||||||
|
data.files.forEach(file => {
|
||||||
|
const row = document.createElement("tr");
|
||||||
|
const checkboxTd = document.createElement("td");
|
||||||
|
const checkbox = document.createElement("input");
|
||||||
|
checkbox.type = "checkbox";
|
||||||
|
checkbox.className = "file-checkbox";
|
||||||
|
checkbox.value = file.name;
|
||||||
|
checkbox.addEventListener("change", updateDeleteSelectedVisibility);
|
||||||
|
checkboxTd.appendChild(checkbox);
|
||||||
|
row.appendChild(checkboxTd);
|
||||||
|
const nameTd = document.createElement("td");
|
||||||
|
nameTd.textContent = file.name;
|
||||||
|
row.appendChild(nameTd);
|
||||||
|
const modifiedTd = document.createElement("td");
|
||||||
|
modifiedTd.textContent = file.modified;
|
||||||
|
row.appendChild(modifiedTd);
|
||||||
|
const uploadedTd = document.createElement("td");
|
||||||
|
uploadedTd.textContent = file.uploaded;
|
||||||
|
row.appendChild(uploadedTd);
|
||||||
|
const sizeTd = document.createElement("td");
|
||||||
|
sizeTd.textContent = file.size;
|
||||||
|
row.appendChild(sizeTd);
|
||||||
|
const uploaderTd = document.createElement("td");
|
||||||
|
uploaderTd.textContent = file.uploader;
|
||||||
|
row.appendChild(uploaderTd);
|
||||||
|
const actionsTd = document.createElement("td");
|
||||||
|
actionsTd.className = "actions-cell";
|
||||||
|
const downloadButton = document.createElement("a");
|
||||||
|
downloadButton.className = "btn btn-sm btn-success";
|
||||||
|
downloadButton.href = folderPath + encodeURIComponent(file.name);
|
||||||
|
downloadButton.download = file.name;
|
||||||
|
downloadButton.textContent = "Download";
|
||||||
|
actionsTd.appendChild(downloadButton);
|
||||||
|
if (canEditFile(file.name)) {
|
||||||
|
const editButton = document.createElement("button");
|
||||||
|
editButton.className = "btn btn-sm btn-primary ml-2";
|
||||||
|
editButton.textContent = "Edit";
|
||||||
|
editButton.addEventListener("click", function () {
|
||||||
|
editFile(file.name, currentFolder);
|
||||||
|
});
|
||||||
|
actionsTd.appendChild(editButton);
|
||||||
|
}
|
||||||
|
row.appendChild(actionsTd);
|
||||||
|
tbody.appendChild(row);
|
||||||
|
});
|
||||||
|
table.appendChild(tbody);
|
||||||
|
fileListContainer.appendChild(table);
|
||||||
|
updateDeleteSelectedVisibility();
|
||||||
|
} else {
|
||||||
|
fileListContainer.textContent = "No files found.";
|
||||||
|
document.getElementById("deleteSelectedBtn").style.display = "none";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error loading file list:", error));
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDeleteSelectedVisibility() {
|
||||||
|
const checkboxes = document.querySelectorAll(".file-checkbox");
|
||||||
|
const deleteBtn = document.getElementById("deleteSelectedBtn");
|
||||||
|
if (checkboxes.length > 0) {
|
||||||
|
deleteBtn.style.display = "inline-block";
|
||||||
|
let anyChecked = false;
|
||||||
|
checkboxes.forEach(chk => {
|
||||||
|
if (chk.checked) anyChecked = true;
|
||||||
|
});
|
||||||
|
deleteBtn.disabled = !anyChecked;
|
||||||
|
} else {
|
||||||
|
deleteBtn.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDeleteSelected(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
const checkboxes = document.querySelectorAll(".file-checkbox:checked");
|
||||||
|
if (checkboxes.length === 0) {
|
||||||
|
alert("No files selected.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!confirm("Are you sure you want to delete the selected files?")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const filesToDelete = Array.from(checkboxes).map(chk => chk.value);
|
||||||
|
fetch("deleteFiles.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ folder: currentFolder, files: filesToDelete })
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
alert("Selected files deleted successfully!");
|
||||||
|
loadFileList(currentFolder);
|
||||||
|
} else {
|
||||||
|
alert("Error: " + (data.error || "Could not delete files"));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error deleting files:", error));
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteSelectedBtn = document.getElementById("deleteSelectedBtn");
|
||||||
|
deleteSelectedBtn.replaceWith(deleteSelectedBtn.cloneNode(true));
|
||||||
|
document.getElementById("deleteSelectedBtn").addEventListener("click", handleDeleteSelected);
|
||||||
|
|
||||||
|
// -----------------------
|
||||||
|
// File Editing Functions
|
||||||
|
// -----------------------
|
||||||
|
window.editFile = function (fileName, folder) {
|
||||||
|
console.log("Edit button clicked for:", fileName);
|
||||||
|
let existingEditor = document.getElementById("editorContainer");
|
||||||
|
if (existingEditor) { existingEditor.remove(); }
|
||||||
|
const folderUsed = folder || currentFolder || "root";
|
||||||
|
const folderPath = (folderUsed === "root") ? "uploads/" : "uploads/" + encodeURIComponent(folderUsed) + "/";
|
||||||
|
fetch(folderPath + encodeURIComponent(fileName) + "?t=" + new Date().getTime())
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) { throw new Error("HTTP error! Status: " + response.status); }
|
||||||
|
return response.text();
|
||||||
|
})
|
||||||
|
.then(content => {
|
||||||
|
const modal = document.createElement("div");
|
||||||
|
modal.id = "editorContainer";
|
||||||
|
modal.classList.add("modal", "editor-modal");
|
||||||
|
modal.innerHTML = `
|
||||||
|
<h3>Editing: ${fileName}</h3>
|
||||||
|
<textarea id="fileEditor" style="width:100%; height:80%; resize:none;">${content}</textarea>
|
||||||
|
<div style="margin-top:10px; text-align:right;">
|
||||||
|
<button onclick="saveFile('${fileName}', '${folderUsed}')" class="btn btn-primary">Save</button>
|
||||||
|
<button onclick="document.getElementById('editorContainer').remove()" class="btn btn-secondary">Close</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
modal.style.display = "block";
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error loading file:", error));
|
||||||
|
};
|
||||||
|
|
||||||
|
window.saveFile = function (fileName, folder) {
|
||||||
|
const editor = document.getElementById("fileEditor");
|
||||||
|
if (!editor) {
|
||||||
|
console.error("Editor not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const folderUsed = folder || currentFolder || "root";
|
||||||
|
const fileDataObj = {
|
||||||
|
fileName: fileName,
|
||||||
|
content: editor.value,
|
||||||
|
folder: folderUsed
|
||||||
|
};
|
||||||
|
fetch("saveFile.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(fileDataObj)
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => {
|
||||||
|
alert(result.success || result.error);
|
||||||
|
document.getElementById("editorContainer")?.remove();
|
||||||
|
loadFileList(folderUsed);
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error saving file:", error));
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------
|
||||||
|
// Upload Form Handling
|
||||||
|
// -----------------------
|
||||||
|
const fileInput = document.getElementById("file");
|
||||||
|
const progressContainer = document.getElementById("uploadProgressContainer");
|
||||||
|
const uploadForm = document.getElementById("uploadFileForm");
|
||||||
|
|
||||||
|
fileInput.addEventListener("change", function () {
|
||||||
|
progressContainer.innerHTML = "";
|
||||||
|
const files = fileInput.files;
|
||||||
|
if (files.length > 0) {
|
||||||
|
const list = document.createElement("ul");
|
||||||
|
list.style.listStyle = "none";
|
||||||
|
list.style.padding = "0";
|
||||||
|
Array.from(files).forEach((file, index) => {
|
||||||
|
const listItem = document.createElement("li");
|
||||||
|
listItem.style.paddingTop = "20px";
|
||||||
|
listItem.style.marginBottom = "10px";
|
||||||
|
listItem.style.display = "flex";
|
||||||
|
listItem.style.alignItems = "center";
|
||||||
|
listItem.style.flexWrap = "wrap";
|
||||||
|
const previewContainer = document.createElement("div");
|
||||||
|
previewContainer.className = "file-preview";
|
||||||
|
displayFilePreview(file, previewContainer);
|
||||||
|
const fileNameDiv = document.createElement("div");
|
||||||
|
fileNameDiv.textContent = file.name;
|
||||||
|
fileNameDiv.style.flexGrow = "1";
|
||||||
|
fileNameDiv.style.marginLeft = "10px";
|
||||||
|
fileNameDiv.style.wordBreak = "break-word";
|
||||||
|
const progressDiv = document.createElement("div");
|
||||||
|
progressDiv.classList.add("progress");
|
||||||
|
progressDiv.style.flex = "0 0 250px";
|
||||||
|
progressDiv.style.marginLeft = "5px";
|
||||||
|
const progressBar = document.createElement("div");
|
||||||
|
progressBar.classList.add("progress-bar");
|
||||||
|
progressBar.style.width = "0%";
|
||||||
|
progressBar.innerText = "0%";
|
||||||
|
progressDiv.appendChild(progressBar);
|
||||||
|
listItem.appendChild(previewContainer);
|
||||||
|
listItem.appendChild(fileNameDiv);
|
||||||
|
listItem.appendChild(progressDiv);
|
||||||
|
listItem.progressBar = progressBar;
|
||||||
|
listItem.startTime = Date.now();
|
||||||
|
list.appendChild(listItem);
|
||||||
|
});
|
||||||
|
progressContainer.appendChild(list);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
uploadForm.addEventListener("submit", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const files = fileInput.files;
|
||||||
|
if (files.length === 0) {
|
||||||
|
alert("No files selected.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const folderToUse = currentFolder || "root";
|
||||||
|
const listItems = progressContainer.querySelectorAll("li");
|
||||||
|
let finishedCount = 0;
|
||||||
|
Array.from(files).forEach((file, index) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file[]", file);
|
||||||
|
formData.append("folder", folderToUse);
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
let currentPercent = 0;
|
||||||
|
xhr.upload.addEventListener("progress", function (e) {
|
||||||
|
if (e.lengthComputable) {
|
||||||
|
currentPercent = Math.round((e.loaded / e.total) * 100);
|
||||||
|
const elapsedTime = (Date.now() - listItems[index].startTime) / 1000;
|
||||||
|
let speedText = "";
|
||||||
|
if (elapsedTime > 0) {
|
||||||
|
const speed = e.loaded / elapsedTime;
|
||||||
|
if (speed < 1024) speedText = speed.toFixed(0) + " B/s";
|
||||||
|
else if (speed < 1048576) speedText = (speed / 1024).toFixed(1) + " KB/s";
|
||||||
|
else speedText = (speed / 1048576).toFixed(1) + " MB/s";
|
||||||
|
}
|
||||||
|
listItems[index].progressBar.style.width = currentPercent + "%";
|
||||||
|
listItems[index].progressBar.innerText = currentPercent + "% (" + speedText + ")";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
xhr.addEventListener("load", function () {
|
||||||
|
if (currentPercent >= 100) {
|
||||||
|
listItems[index].progressBar.innerText = "Done";
|
||||||
|
}
|
||||||
|
finishedCount++;
|
||||||
|
console.log("Upload response for file", file.name, xhr.responseText);
|
||||||
|
if (finishedCount === files.length) {
|
||||||
|
loadFileList(folderToUse);
|
||||||
|
fileInput.value = "";
|
||||||
|
setTimeout(() => { progressContainer.innerHTML = ""; }, 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
xhr.addEventListener("error", function () {
|
||||||
|
listItems[index].progressBar.innerText = "Error";
|
||||||
|
});
|
||||||
|
xhr.open("POST", "upload.php", true);
|
||||||
|
xhr.send(formData);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user