Improve PDF preview and input focus behaviors

This commit is contained in:
Ryan
2025-04-30 00:53:44 -04:00
committed by GitHub
parent 2e4dbe7f7f
commit 274bedd186
5 changed files with 101 additions and 55 deletions

View File

@@ -1,5 +1,15 @@
# Changelog # Changelog
## Changes 4/30/2025 v1.2.8
- **Added** PDF preview in `filePreview.js` (the `extension === "pdf"` block): replaced in-modal `<embed>` with `window.open(urlWithTs, "_blank")` and closed the modal to avoid CSP `frame-ancestors 'none'` restrictions.
- **Added** `autofocus` attribute to the login forms username input (`#loginUsername`) so the cursor is ready for typing on page load.
- **Enhanced** login initialization with a `DOMContentLoaded` fallback that calls `loginUsername.focus()` (via `setTimeout`) if needed.
- **Set** focus to the “New Username” field (`#newUsername`) when entering setup mode, hiding the login form and showing the Add-User modal.
- **Implemented** Enter-key support in setup mode by attaching `attachEnterKeyListener("addUserModal", "saveUserBtn")`, allowing users to press Enter to submit the Add-User form.
---
## Changes 4/28/2025 ## Changes 4/28/2025
**Added** **Added**
@@ -28,7 +38,7 @@
- OpenAPI annotations for both endpoints updated to require `expirationValue` + `expirationUnit` (enum: seconds, minutes, hours, days) - OpenAPI annotations for both endpoints updated to require `expirationValue` + `expirationUnit` (enum: seconds, minutes, hours, days)
## Changes 4/27/2025 1.2.7 ## Changes 4/27/2025 v1.2.7
- **Select-All** checkbox now correctly toggles all `.file-checkbox` inputs - **Select-All** checkbox now correctly toggles all `.file-checkbox` inputs
- Updated `toggleAllCheckboxes(masterCheckbox)` to call `updateRowHighlight()` on each row so selections get the `.row-selected` highlight - Updated `toggleAllCheckboxes(masterCheckbox)` to call `updateRowHighlight()` on each row so selections get the `.row-selected` highlight

View File

@@ -182,7 +182,7 @@
<form id="authForm" method="post"> <form id="authForm" method="post">
<div class="form-group"> <div class="form-group">
<label for="loginUsername" data-i18n-key="user">User:</label> <label for="loginUsername" data-i18n-key="user">User:</label>
<input type="text" class="form-control" id="loginUsername" name="username" required /> <input type="text" class="form-control" id="loginUsername" name="username" required autofocus />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="loginPassword" data-i18n-key="password">Password:</label> <label for="loginPassword" data-i18n-key="password">Password:</label>
@@ -442,18 +442,30 @@
<div id="addUserModal" class="modal"> <div id="addUserModal" class="modal">
<div class="modal-content"> <div class="modal-content">
<h3 data-i18n-key="create_new_user_title">Create New User</h3> <h3 data-i18n-key="create_new_user_title">Create New User</h3>
<label for="newUsername" data-i18n-key="username">Username:</label> <!-- 1) Add a form around these fields -->
<input type="text" id="newUsername" class="form-control" /> <form id="addUserForm">
<label for="addUserPassword" data-i18n-key="password">Password:</label> <label for="newUsername" data-i18n-key="username">Username:</label>
<input type="password" id="addUserPassword" class="form-control" /> <input type="text" id="newUsername" class="form-control" required />
<div id="adminCheckboxContainer">
<input type="checkbox" id="isAdmin" /> <label for="addUserPassword" data-i18n-key="password">Password:</label>
<label for="isAdmin" data-i18n-key="grant_admin">Grant Admin Access</label> <input type="password" id="addUserPassword" class="form-control" required />
</div>
<div class="button-container"> <div id="adminCheckboxContainer">
<button id="cancelUserBtn" class="btn btn-secondary" data-i18n-key="cancel">Cancel</button> <input type="checkbox" id="isAdmin" />
<button id="saveUserBtn" class="btn btn-primary" data-i18n-key="save_user">Save User</button> <label for="isAdmin" data-i18n-key="grant_admin">Grant Admin Access</label>
</div> </div>
<div class="button-container">
<!-- Cancel stays type="button" -->
<button type="button" id="cancelUserBtn" class="btn btn-secondary" data-i18n-key="cancel">
Cancel
</button>
<!-- Save becomes type="submit" -->
<button type="submit" id="saveUserBtn" class="btn btn-primary" data-i18n-key="save_user">
Save User
</button>
</div>
</form>
</div> </div>
</div> </div>
<div id="removeUserModal" class="modal"> <div id="removeUserModal" class="modal">

View File

@@ -125,10 +125,17 @@ function updateItemsPerPageSelect() {
} }
} }
function updateLoginOptionsUI({ disableFormLogin, disableBasicAuth, disableOIDCLogin }) { function updateLoginOptionsUI({ disableFormLogin, disableBasicAuth, disableOIDCLogin }) {
const authForm = document.getElementById("authForm"); const authForm = document.getElementById("authForm");
if
if (authForm) authForm.style.display = disableFormLogin ? "none" : "block"; (authForm) {
authForm.style.display = disableFormLogin ? "none" : "block";
setTimeout(() => {
const loginInput = document.getElementById('loginUsername');
if (loginInput) loginInput.focus();
}, 0);
}
const basicAuthLink = document.querySelector("a[href='/api/auth/login_basic.php']"); const basicAuthLink = document.querySelector("a[href='/api/auth/login_basic.php']");
if (basicAuthLink) basicAuthLink.style.display = disableBasicAuth ? "none" : "inline-block"; if (basicAuthLink) basicAuthLink.style.display = disableBasicAuth ? "none" : "inline-block";
const oidcLoginBtn = document.getElementById("oidcLoginBtn"); const oidcLoginBtn = document.getElementById("oidcLoginBtn");
@@ -187,7 +194,7 @@ function updateAuthenticatedUI(data) {
toggleVisibility("mainOperations", true); toggleVisibility("mainOperations", true);
toggleVisibility("uploadFileForm", true); toggleVisibility("uploadFileForm", true);
toggleVisibility("fileListContainer", true); toggleVisibility("fileListContainer", true);
attachEnterKeyListener("addUserModal", "saveUserBtn"); //attachEnterKeyListener("addUserModal", "saveUserBtn");
attachEnterKeyListener("removeUserModal", "deleteUserBtn"); attachEnterKeyListener("removeUserModal", "deleteUserBtn");
attachEnterKeyListener("changePasswordModal", "saveNewPasswordBtn"); attachEnterKeyListener("changePasswordModal", "saveNewPasswordBtn");
document.querySelector(".header-buttons").style.visibility = "visible"; document.querySelector(".header-buttons").style.visibility = "visible";
@@ -443,34 +450,46 @@ function initAuth() {
toggleVisibility("addUserModal", true); toggleVisibility("addUserModal", true);
document.getElementById("newUsername").focus(); document.getElementById("newUsername").focus();
}); });
document.getElementById("saveUserBtn").addEventListener("click", function () {
const newUsername = document.getElementById("newUsername").value.trim(); // remove your old saveUserBtn click-handler…
const newPassword = document.getElementById("addUserPassword").value.trim();
const isAdmin = document.getElementById("isAdmin").checked; // instead:
if (!newUsername || !newPassword) { const addUserForm = document.getElementById("addUserForm");
showToast("Username and password are required!"); addUserForm.addEventListener("submit", function (e) {
return; e.preventDefault(); // stop the browser from reloading the page
}
let url = "/api/addUser.php"; const newUsername = document.getElementById("newUsername").value.trim();
if (window.setupMode) url += "?setup=1"; const newPassword = document.getElementById("addUserPassword").value.trim();
fetchWithCsrf(url, { const isAdmin = document.getElementById("isAdmin").checked;
method: "POST",
credentials: "include", if (!newUsername || !newPassword) {
headers: { "Content-Type": "application/json" }, showToast("Username and password are required!");
body: JSON.stringify({ username: newUsername, password: newPassword, isAdmin }) return;
}
let url = "/api/addUser.php";
if (window.setupMode) url += "?setup=1";
fetchWithCsrf(url, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: newUsername, password: newPassword, isAdmin })
})
.then(r => r.json())
.then(data => {
if (data.success) {
showToast("User added successfully!");
closeAddUserModal();
checkAuthentication(false);
} else {
showToast("Error: " + (data.error || "Could not add user"));
}
}) })
.then(response => response.json()) .catch(() => {
.then(data => { showToast("Error: Could not add user");
if (data.success) { });
showToast("User added successfully!"); });
closeAddUserModal();
checkAuthentication(false);
} else {
showToast("Error: " + (data.error || "Could not add user"));
}
})
.catch(() => { });
});
document.getElementById("cancelUserBtn").addEventListener("click", closeAddUserModal); document.getElementById("cancelUserBtn").addEventListener("click", closeAddUserModal);
document.getElementById("removeUserBtn").addEventListener("click", function () { document.getElementById("removeUserBtn").addEventListener("click", function () {

View File

@@ -3,7 +3,7 @@ import { sendRequest } from './networkUtils.js';
import { t, applyTranslations, setLocale } from './i18n.js'; import { t, applyTranslations, setLocale } from './i18n.js';
import { loadAdminConfigFunc } from './auth.js'; import { loadAdminConfigFunc } from './auth.js';
const version = "v1.2.7"; // Update this version string as needed const version = "v1.2.8"; // Update this version string as needed
const adminTitle = `${t("admin_panel")} <small style="font-size: 12px; color: gray;">${version}</small>`; const adminTitle = `${t("admin_panel")} <small style="font-size: 12px; color: gray;">${version}</small>`;
let lastLoginData = null; let lastLoginData = null;

View File

@@ -416,16 +416,21 @@ export function previewFile(fileUrl, fileName) {
} }
} else { } else {
// Handle non-image file previews. // Handle non-image file previews.
if (extension === "pdf") { if (extension === "pdf") {
const embed = document.createElement("embed"); // build a cachebusted URL
const separator = fileUrl.indexOf('?') === -1 ? '?' : '&'; const separator = fileUrl.includes('?') ? '&' : '?';
embed.src = fileUrl + separator + 't=' + new Date().getTime(); const urlWithTs = fileUrl + separator + 't=' + Date.now();
embed.type = "application/pdf";
embed.style.width = "80vw"; // open in a new tab (avoids CSP frame-ancestors)
embed.style.height = "80vh"; window.open(urlWithTs, "_blank");
embed.style.border = "none";
container.appendChild(embed); // tear down the just-created modal
} else if (/\.(mp4|mkv|webm|mov|ogv)$/i.test(fileName)) { const modal = document.getElementById("filePreviewModal");
if (modal) modal.remove();
// stop further preview logic
return;
} else if (/\.(mp4|mkv|webm|mov|ogv)$/i.test(fileName)) {
const video = document.createElement("video"); const video = document.createElement("video");
video.src = fileUrl; video.src = fileUrl;
video.controls = true; video.controls = true;