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($('