From 960b27b41466a2ad096387c9ff04c1886c824ab8 Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 7 Mar 2025 03:22:20 -0500 Subject: [PATCH] new features and refactor --- README.md | 54 ++- auth.js | 168 +++++++-- displayFileList.js | 181 ---------- domUtils.js | 70 ++++ fileManager.js | 483 +++++++++++++++++++++++++ folderManager.js | 215 +++++++++++ getFileList.php | 3 + index.html | 6 +- main.js | 48 +++ networkUtils.js | 22 ++ renameFile.php | 62 ++++ styles.css | 61 +++- upload.js | 310 ++++++++++------ utils.js | 880 --------------------------------------------- 14 files changed, 1338 insertions(+), 1225 deletions(-) delete mode 100644 displayFileList.js create mode 100644 domUtils.js create mode 100644 fileManager.js create mode 100644 folderManager.js create mode 100644 main.js create mode 100644 networkUtils.js create mode 100644 renameFile.php delete mode 100644 utils.js diff --git a/README.md b/README.md index 44044f6..f9409cd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,46 @@ - - - main screen +**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** 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($('