diff --git a/README.md b/README.md
index 44044f6..f9409cd 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,46 @@
-
-
-
+**Changes 3/7/2025:***
+
+- Module Separation & ES6 Conversion
+ - networkUtils.js: For handling HTTP requests.
+ - domUtils.js: For DOM manipulation functions (e.g. toggleVisibility, escapeHTML, toggleAllCheckboxes, and file action button updates).
+ - fileManager.js: For file operations, rendering the file list, sorting, editing, renaming, and pagination.
+ - folderManager.js: For folder-related operations (loading folder lists, renaming/deleting folders, etc.).
+ - upload.js: For handling file uploads and progress display.
+ - auth.js: For authentication and user management.
+ - Converted all modules to ES6
+
+- File List Rendering & Pagination in fileManager.js
+ - Implemented Pagination
+ - Added global settings (window.itemsPerPage and window.currentPage) with defaults (10 items per page).
+ - Modified renderFileTable() to calculate the current slice of files and render pagination controls (with “Prev”/“Next” buttons and an items-per-page selector).
+ - Reworked Sorting
+ - updated sortFiles() to re-render the table on sorting.
+ - Implemented sorting for non-date columns by converting strings to lowercase.
+ - Date sorting improvements
+
+- File Upload Enhancements in upload.js
+ - Maintained individual progress tracking for the first 10 files while still uploading all selected files.
+ - Implemented logic to refresh the file list instantly after uploads finish.
+ - Configured the progress list to remain visible for 10 seconds after the file list refresh so users can verify the upload status.
+ - Ensured that after refreshing the file list, event listeners for actions (delete, copy, move) are reattached.
+ - File upload error handling and display
+
+- File Action Buttons & Checkbox Handling (domUtils.js and fileManager.js)
+ - Rewrote the updateFileActionButtons()
+ - Removed duplicate or conflicting logic from renderFileTable() and initFileActions() that previously managed button visibility.
+ - Adjusted toggleAllCheckboxes() and toggleDeleteButton() so they call updateFileActionButtons() to maintain a single source of truth.
+
+- Rename Functionality
+ - Updated the Actions column in the file table to always include a “Rename” button for each file.
+ - Implemented renameFile()
+
+- Responsive Behavior & Additional UI Tweaks
+ - Added CSS media queries to hide less critical columns (Date Modified, Upload Date, File Size, Uploader) on smaller screens.
+ - Adjusted margins on file preview images and file icons.
+ - Improved header centering and button styling.
+
**Changes 3/4/2025:**
Copy & Move functionality added
Header Layout
@@ -11,7 +49,6 @@
CSS Consolidation
assets folder
-
**Changes 3/3/2025:**
folder management added
some refactoring
@@ -106,20 +143,14 @@ This project is a lightweight, secure web application for uploading, editing, an
This multi-file uploader with editing and user management is ideal for scenarios involving document management, image galleries, firmware updates, and more.
---
+
- **Login Page**
-
-
-
-
-
-
based off of:
https://github.com/sensboston/uploader
-
## Prerequisites
- Apache2, configured, up and running
@@ -127,4 +158,3 @@ https://github.com/sensboston/uploader
- Required PHP extensions: `php-json`, `php-curl`
...........
-
diff --git a/auth.js b/auth.js
index 03c9c37..cec3a57 100644
--- a/auth.js
+++ b/auth.js
@@ -1,15 +1,18 @@
// auth.js
-import { sendRequest, toggleVisibility } from './utils.js';
-let setupMode = false; // Declare setupMode here
+import { sendRequest } from './networkUtils.js';
+import { toggleVisibility } from './domUtils.js';
+// Import loadFileList from fileManager.js to refresh the file list upon login.
+import { loadFileList } from './fileManager.js';
-document.addEventListener("DOMContentLoaded", function () {
- // Hide file list and upload form on load.
- toggleVisibility("fileListContainer", false);
- toggleVisibility("uploadFileForm", false);
-
- checkAuthentication();
+export function initAuth() {
+ // On initial load, show the login form and hide the main operations.
+ toggleVisibility("loginForm", true);
+ toggleVisibility("mainOperations", false);
+ // Ensure header buttons are hidden.
+ document.querySelector(".header-buttons").style.visibility = "hidden";
+ // Set up the authentication form listener.
document.getElementById("authForm").addEventListener("submit", function (event) {
event.preventDefault();
const formData = {
@@ -22,58 +25,179 @@ document.addEventListener("DOMContentLoaded", function () {
console.log("Login response:", data);
if (data.success) {
console.log("Login successful.");
+ // On successful login, hide the login form and show main operations.
toggleVisibility("loginForm", false);
+ toggleVisibility("mainOperations", true);
toggleVisibility("uploadFileForm", true);
toggleVisibility("fileListContainer", true);
- checkAuthentication(); // Recheck authentication to update UI.
+ document.querySelector(".header-buttons").style.visibility = "visible";
+ // Refresh the file list immediately using the current folder.
+ loadFileList(window.currentFolder || "root");
+ // Optionally, you can also call checkAuthentication() to update UI further.
+ checkAuthentication();
} else {
alert("Login failed: " + (data.error || "Unknown error"));
}
})
.catch(error => console.error("Error logging in:", error));
});
-});
+
+ // Set up the logout button.
+ document.getElementById("logoutBtn").addEventListener("click", function () {
+ fetch("logout.php", { method: "POST" })
+ .then(() => window.location.reload(true))
+ .catch(error => console.error("Logout error:", error));
+ });
+
+ // Set up Add User functionality.
+ document.getElementById("addUserBtn").addEventListener("click", function () {
+ resetUserForm();
+ toggleVisibility("addUserModal", true);
+ });
+
+ 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 (window.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();
+ });
+
+ // Set up Remove User functionality.
+ document.getElementById("removeUserBtn").addEventListener("click", function () {
+ loadUserList();
+ toggleVisibility("removeUserModal", true);
+ });
+
+ 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();
+ });
+}
export function checkAuthentication() {
sendRequest("checkAuth.php")
.then(data => {
console.log("Authentication check:", data);
if (data.setup) {
- setupMode = true;
- // In setup mode, hide all sections except the Add User modal.
+ window.setupMode = true;
+ // In setup mode, hide login and main operations; show Add User modal.
toggleVisibility("loginForm", false);
- toggleVisibility("uploadFileForm", false);
- toggleVisibility("fileListContainer", false);
+ toggleVisibility("mainOperations", false);
document.querySelector(".header-buttons").style.visibility = "hidden";
toggleVisibility("addUserModal", true);
return;
} else {
- setupMode = false;
+ window.setupMode = false;
}
if (data.authenticated) {
toggleVisibility("loginForm", false);
+ toggleVisibility("mainOperations", true);
toggleVisibility("uploadFileForm", true);
toggleVisibility("fileListContainer", true);
- if (typeof loadFileList === "function") {
- loadFileList();
- }
+ document.querySelector(".header-buttons").style.visibility = "visible";
} else {
toggleVisibility("loginForm", true);
+ toggleVisibility("mainOperations", false);
toggleVisibility("uploadFileForm", false);
toggleVisibility("fileListContainer", false);
+ document.querySelector(".header-buttons").style.visibility = "hidden";
}
})
.catch(error => console.error("Error checking authentication:", error));
}
window.checkAuthentication = checkAuthentication;
-// Helper functions for the Add User modal.
+// Helper functions for auth modals.
+function resetUserForm() {
+ document.getElementById("newUsername").value = "";
+ document.getElementById("newPassword").value = "";
+}
+
function closeAddUserModal() {
toggleVisibility("addUserModal", false);
resetUserForm();
}
-function resetUserForm() {
- document.getElementById("newUsername").value = "";
- document.getElementById("newPassword").value = "";
+function closeRemoveUserModal() {
+ toggleVisibility("removeUserModal", false);
+ 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));
}
diff --git a/displayFileList.js b/displayFileList.js
deleted file mode 100644
index 0587c29..0000000
--- a/displayFileList.js
+++ /dev/null
@@ -1,181 +0,0 @@
-// displayFileList.js
-
-import { sendRequest, toggleVisibility } from './utils.js';
-
-let fileData = [];
-let sortOrder = { column: "uploaded", ascending: false };
-export let currentFolder = "root"; // Global current folder
-
-export function loadFileList() {
- sendRequest("checkAuth.php")
- .then(authData => {
- if (!authData.authenticated) {
- console.warn("User not authenticated, hiding file list.");
- toggleVisibility("fileListContainer", false);
- return;
- }
- toggleVisibility("fileListContainer", true);
- return sendRequest("getFileList.php?folder=" + encodeURIComponent(currentFolder));
- })
- .then(data => {
- if (!data) return;
- if (data.error) {
- document.getElementById("fileList").innerHTML = `
Error: ${data.error}
`;
- return;
- }
- 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));
-}
-
-export function toggleDeleteButton() {
- const selectedFiles = document.querySelectorAll(".file-checkbox:checked");
- const deleteBtn = document.getElementById("deleteSelectedBtn");
- const copyBtn = document.getElementById("copySelectedBtn");
- const moveBtn = document.getElementById("moveSelectedBtn");
- const disabled = selectedFiles.length === 0;
- deleteBtn.disabled = disabled;
- if (copyBtn) copyBtn.disabled = disabled;
- if (moveBtn) moveBtn.disabled = disabled;
-}
-
-export function toggleAllCheckboxes(source) {
- const checkboxes = document.querySelectorAll(".file-checkbox");
- checkboxes.forEach(checkbox => checkbox.checked = source.checked);
- toggleDeleteButton();
-}
-
-export function deleteSelectedFiles() {
- const selectedFiles = Array.from(document.querySelectorAll(".file-checkbox:checked"))
- .map(checkbox => checkbox.value);
- if (selectedFiles.length === 0) {
- alert("No files selected for deletion.");
- return;
- }
- if (!confirm("Are you sure you want to delete the selected files?")) {
- return;
- }
- sendRequest("deleteFiles.php", "POST", { files: selectedFiles })
- .then(result => {
- alert(result.success || result.error);
- loadFileList();
- })
- .catch(error => console.error("Error deleting files:", error));
-}
-
-document.addEventListener("DOMContentLoaded", function () {
- loadFileList();
- loadCopyMoveFolderList();
-
- const deleteBtn = document.getElementById("deleteSelectedBtn");
- if (deleteBtn) {
- deleteBtn.addEventListener("click", deleteSelectedFiles);
- }
-
- const copyBtn = document.getElementById("copySelectedBtn");
- const moveBtn = document.getElementById("moveSelectedBtn");
- if (copyBtn) {
- copyBtn.addEventListener("click", copySelectedFiles);
- }
- if (moveBtn) {
- moveBtn.addEventListener("click", moveSelectedFiles);
- }
-});
-
-
-// ===== NEW CODE: Copy & Move Functions =====
-
-// Copy selected files to a target folder
-export function copySelectedFiles() {
- const selectedFiles = Array.from(document.querySelectorAll(".file-checkbox:checked"))
- .map(checkbox => checkbox.value);
- const targetFolder = document.getElementById("copyMoveFolderSelect").value;
- if (selectedFiles.length === 0) {
- alert("Please select at least one file to copy.");
- return;
- }
- if (!targetFolder) {
- alert("Please select a target folder.");
- return;
- }
- if (currentFolder === targetFolder) {
- alert("Cannot copy files to the same folder.");
- return;
- }
- // Send the correct keys
- sendRequest("copyFiles.php", "POST", {
- source: currentFolder,
- destination: targetFolder,
- files: selectedFiles
- })
- .then(result => {
- alert(result.success || result.error);
- loadFileList();
- })
- .catch(error => console.error("Error copying files:", error));
-}
-
-export function moveSelectedFiles() {
- const selectedFiles = Array.from(document.querySelectorAll(".file-checkbox:checked"))
- .map(checkbox => checkbox.value);
- const targetFolder = document.getElementById("copyMoveFolderSelect").value;
- if (selectedFiles.length === 0) {
- alert("Please select at least one file to move.");
- return;
- }
- if (!targetFolder) {
- alert("Please select a target folder.");
- return;
- }
- if (currentFolder === targetFolder) {
- alert("Cannot move files to the same folder.");
- return;
- }
- console.log("Payload:", {
- source: currentFolder,
- destination: targetFolder,
- files: selectedFiles
- });
- sendRequest("moveFiles.php", "POST", {
- source: currentFolder,
- destination: targetFolder,
- files: selectedFiles
- })
- .then(result => {
- alert(result.success || result.error);
- loadFileList();
- })
- .catch(error => console.error("Error moving files:", error));
-}
-
-
-// Populate the Copy/Move folder dropdown
-export function loadCopyMoveFolderList() {
- $.get('getFolderList.php', function (response) {
- const folderSelect = $('#copyMoveFolderSelect');
- folderSelect.empty();
- // Always add a "Root" option as the default.
- folderSelect.append($('