UI improvements, contact windows, fixed #196

This commit is contained in:
Pinga 2025-02-18 13:43:41 +02:00
parent 5ac0e9f1fa
commit 184322e2d2
6 changed files with 493 additions and 360 deletions

View file

@ -266,4 +266,30 @@ class ProfileController extends Controller
Auth::logoutEverywhereElse(); Auth::logoutEverywhereElse();
} }
public function tokenWell(Request $request, Response $response)
{
global $container;
$csrf = $container->get('csrf');
// Get CSRF token name and value
$csrfTokenName = $csrf->getTokenName();
$csrfTokenValue = $csrf->getTokenValue();
// Check if tokens exist
if (!$csrfTokenName || !$csrfTokenValue) {
$errorResponse = json_encode(['error' => 'CSRF tokens not found']);
$response->getBody()->write($errorResponse);
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
}
// Create JSON response in the expected format
$csrfResponse = json_encode([
$csrfTokenName => $csrfTokenValue
]);
// Write response body and return with JSON header
$response->getBody()->write($csrfResponse);
return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
}
} }

View file

@ -302,6 +302,10 @@ $csrfMiddleware = function ($request, $handler) use ($container) {
if ($path && $path === '/clear-cache') { if ($path && $path === '/clear-cache') {
return $handler->handle($request); return $handler->handle($request);
} }
if ($path && $path === '/token-well') {
$csrf->generateToken();
return $handler->handle($request);
}
// If not skipped, apply the CSRF Guard // If not skipped, apply the CSRF Guard
return $csrf->process($request, $handler); return $csrf->process($request, $handler);

View file

@ -491,114 +491,108 @@
<link href="/assets/css/tabulator.min.css" rel="stylesheet"> <link href="/assets/css/tabulator.min.css" rel="stylesheet">
<script src="/assets/js/tabulator.min.js" defer></script> <script src="/assets/js/tabulator.min.js" defer></script>
<script> <script>
document.getElementById("cancelButton").addEventListener("click", function () { let addContactTargetInputId = null;
location.reload();
});
let addContactTargetInputId = null; // Capture the target input when opening "Add Contact" modal
document.addEventListener("click", function (e) {
// Capture the target input when opening "Add Contact" modal let openBtn = e.target.closest('.open-add-modal');
document.addEventListener("click", function (e) { if (openBtn) {
let openBtn = e.target.closest('.open-add-modal'); addContactTargetInputId = openBtn.getAttribute('data-input');
if (openBtn) {
addContactTargetInputId = openBtn.getAttribute('data-input');
}
});
document.getElementById("addContactForm").addEventListener("submit", function (e) {
e.preventDefault(); // Prevent full-page reload
let form = this;
let formData = new FormData(form);
fetch("/contact/create-api", {
method: "POST",
body: formData,
headers: {
"Accept": "application/json"
} }
}) });
.then(response => {
return response.json().then(data => {
if (!response.ok) {
showErrorMessage(data.error || `Server Error (${response.status})`);
// Keep the modal open when there's an error document.getElementById("addContactForm").addEventListener("submit", function (e) {
e.preventDefault(); // Prevent full-page reload
let form = this;
let formData = new FormData(form);
fetch("/contact/create-api", {
method: "POST",
body: formData,
headers: {
"Accept": "application/json"
}
})
.then(response => {
return response.json().then(data => {
if (!response.ok) {
showErrorMessage(data.error || `Server Error (${response.status})`);
// Keep the modal open when there's an error
let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) {
modalInstance._isShown = true;
}
throw new Error(data.error || `Server Error (${response.status})`);
}
return data;
});
})
.then(data => {
console.log("✅ Contact Added:", data);
if (data.identifier && addContactTargetInputId) {
let targetInput = document.getElementById(addContactTargetInputId);
if (targetInput) {
targetInput.value = data.identifier;
}
}
// ✅ Only close modal if form submission is successful
if (data.identifier) {
let modalElement = document.getElementById("addContactModal"); let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement); let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) { if (modalInstance) {
modalInstance._isShown = true; modalInstance.hide();
} }
throw new Error(data.error || `Server Error (${response.status})`);
}
return data;
});
})
.then(data => {
console.log("✅ Contact Added:", data);
if (data.identifier && addContactTargetInputId) { // Reset the form and clear errors after success
let targetInput = document.getElementById(addContactTargetInputId); form.reset();
if (targetInput) { addContactTargetInputId = null;
targetInput.value = data.identifier; document.getElementById("addContactError").classList.add("d-none");
} }
} })
.catch(error => {
console.error("❌ Error adding contact:", error.message);
showErrorMessage(error.message);
// ✅ Only close modal if form submission is successful refreshCsrfToken();
if (data.identifier) {
// ❌ Prevent modal from closing on error
let modalElement = document.getElementById("addContactModal"); let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement); let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) { if (modalInstance) {
modalInstance.hide(); modalInstance._isShown = true; // Keep modal open
} }
});
// Reset the form and clear errors after success
form.reset();
addContactTargetInputId = null;
document.getElementById("addContactError").classList.add("d-none");
}
})
.catch(error => {
console.error("❌ Error adding contact:", error.message);
showErrorMessage(error.message);
// ❌ Prevent modal from closing on error
let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) {
modalInstance._isShown = true; // Keep modal open
}
}); });
});
// Function to show error messages inside the modal // Function to show error messages inside the modal
function showErrorMessage(message) { function showErrorMessage(message) {
let errorContainer = document.getElementById("addContactError"); let errorContainer = document.getElementById("addContactError");
let submitButton = document.querySelector("#addContactModal button[type='submit']"); let submitButton = document.querySelector("#addContactModal button[type='submit']");
if (!errorContainer) {
let form = document.getElementById("addContactForm");
errorContainer = document.createElement("div");
errorContainer.id = "addContactError";
errorContainer.className = "alert alert-danger mt-2";
form.prepend(errorContainer);
}
errorContainer.innerText = message;
errorContainer.classList.remove("d-none"); // Ensure it's visible
// 🔹 Scroll to top of modal when error occurs
let modalBody = document.querySelector("#addContactModal .modal-body");
if (modalBody) {
modalBody.scrollTop = 0;
}
if (!errorContainer) {
let form = document.getElementById("addContactForm");
errorContainer = document.createElement("div");
errorContainer.id = "addContactError";
errorContainer.className = "alert alert-danger mt-2";
form.prepend(errorContainer);
} }
errorContainer.innerText = message;
errorContainer.classList.remove("d-none"); // Ensure it's visible
// 🔹 Scroll to top of modal when error occurs
let modalBody = document.querySelector("#addContactModal .modal-body");
if (modalBody) {
modalBody.scrollTop = 0;
}
// 🔹 Disable submit button on error
if (submitButton) {
submitButton.disabled = true;
}
}
let targetInputId = null; let targetInputId = null;
// Ensure target input is set correctly before the modal opens // Ensure target input is set correctly before the modal opens
@ -638,10 +632,80 @@ function showErrorMessage(message) {
targetInputId = null; targetInputId = null;
}); });
function generateAuthInfoC() {
const length = 16;
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let retVal = "";
let digitCount = 0;
// Generate initial random string
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * charset.length);
const char = charset.charAt(randomIndex);
retVal += char;
if (char >= '0' && char <= '9') {
digitCount++;
}
}
// Ensure there are at least two digits in the string
while (digitCount < 2) {
// Replace a non-digit character at a random position with a digit
const replacePosition = Math.floor(Math.random() * length);
if (!(retVal[replacePosition] >= '0' && retVal[replacePosition] <= '9')) {
const randomDigit = Math.floor(Math.random() * 10); // Generate a digit from 0 to 9
retVal = retVal.substring(0, replacePosition) + randomDigit + retVal.substring(replacePosition + 1);
digitCount++;
}
}
return retVal;
}
async function refreshCsrfToken() {
try {
let response = await fetch('/token-well', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({})
});
if (!response.ok) {
console.error("Failed to fetch CSRF token:", response.status);
return;
}
let data = await response.json();
let csrfNameField = document.querySelector('#addContactModal input[name="csrf_name"]');
let csrfValueField = document.querySelector('#addContactModal input[name="csrf_value"]');
if (csrfNameField && csrfValueField) {
csrfNameField.value = Object.keys(data)[0]; // Set the token name
csrfValueField.value = Object.values(data)[0]; // Set the token value
}
} catch (error) {
console.error("Error refreshing CSRF token:", error);
}
}
document.getElementById("addContactModal").addEventListener("show.bs.modal", function () {
document.getElementById('contactid').value = generateAuthInfoC();
document.getElementById('authInfoc').value = generateAuthInfoC();
});
var table; var table;
document.addEventListener("DOMContentLoaded", function(){ document.addEventListener("DOMContentLoaded", function(){
// Only refresh CSRF token when opening the "Add Contact" modal
let addContactModal = document.getElementById("addContactModal");
if (addContactModal) {
addContactModal.addEventListener("show.bs.modal", refreshCsrfToken);
}
const toggleLocCheckbox = document.getElementById('toggleLoc'); const toggleLocCheckbox = document.getElementById('toggleLoc');
const localizedSection = document.getElementById('localizedInfo'); const localizedSection = document.getElementById('localizedInfo');
@ -661,36 +725,6 @@ function showErrorMessage(message) {
const authInfoInput = document.getElementById('authInfoc'); const authInfoInput = document.getElementById('authInfoc');
authInfoInput.value = generateAuthInfoC(); authInfoInput.value = generateAuthInfoC();
function generateAuthInfoC() {
const length = 16;
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let retVal = "";
let digitCount = 0;
// Generate initial random string
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * charset.length);
const char = charset.charAt(randomIndex);
retVal += char;
if (char >= '0' && char <= '9') {
digitCount++;
}
}
// Ensure there are at least two digits in the string
while (digitCount < 2) {
// Replace a non-digit character at a random position with a digit
const replacePosition = Math.floor(Math.random() * length);
if (!(retVal[replacePosition] >= '0' && retVal[replacePosition] <= '9')) {
const randomDigit = Math.floor(Math.random() * 10); // Generate a digit from 0 to 9
retVal = retVal.substring(0, replacePosition) + randomDigit + retVal.substring(replacePosition + 1);
digitCount++;
}
}
return retVal;
}
function contactLinkFormatter(cell){ function contactLinkFormatter(cell){
var value = cell.getValue(); var value = cell.getValue();
return `<a href="/contact/view/${value}" style="font-weight:bold;">${value}</a>`; return `<a href="/contact/view/${value}" style="font-weight:bold;">${value}</a>`;

View file

@ -615,114 +615,108 @@
<link href="/assets/css/tabulator.min.css" rel="stylesheet"> <link href="/assets/css/tabulator.min.css" rel="stylesheet">
<script src="/assets/js/tabulator.min.js" defer></script> <script src="/assets/js/tabulator.min.js" defer></script>
<script> <script>
document.getElementById("cancelButton").addEventListener("click", function () { let addContactTargetInputId = null;
location.reload();
});
let addContactTargetInputId = null; // Capture the target input when opening "Add Contact" modal
document.addEventListener("click", function (e) {
// Capture the target input when opening "Add Contact" modal let openBtn = e.target.closest('.open-add-modal');
document.addEventListener("click", function (e) { if (openBtn) {
let openBtn = e.target.closest('.open-add-modal'); addContactTargetInputId = openBtn.getAttribute('data-input');
if (openBtn) {
addContactTargetInputId = openBtn.getAttribute('data-input');
}
});
document.getElementById("addContactForm").addEventListener("submit", function (e) {
e.preventDefault(); // Prevent full-page reload
let form = this;
let formData = new FormData(form);
fetch("/contact/create-api", {
method: "POST",
body: formData,
headers: {
"Accept": "application/json"
} }
}) });
.then(response => {
return response.json().then(data => {
if (!response.ok) {
showErrorMessage(data.error || `Server Error (${response.status})`);
// Keep the modal open when there's an error document.getElementById("addContactForm").addEventListener("submit", function (e) {
e.preventDefault(); // Prevent full-page reload
let form = this;
let formData = new FormData(form);
fetch("/contact/create-api", {
method: "POST",
body: formData,
headers: {
"Accept": "application/json"
}
})
.then(response => {
return response.json().then(data => {
if (!response.ok) {
showErrorMessage(data.error || `Server Error (${response.status})`);
// Keep the modal open when there's an error
let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) {
modalInstance._isShown = true;
}
throw new Error(data.error || `Server Error (${response.status})`);
}
return data;
});
})
.then(data => {
console.log("✅ Contact Added:", data);
if (data.identifier && addContactTargetInputId) {
let targetInput = document.getElementById(addContactTargetInputId);
if (targetInput) {
targetInput.value = data.identifier;
}
}
// ✅ Only close modal if form submission is successful
if (data.identifier) {
let modalElement = document.getElementById("addContactModal"); let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement); let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) { if (modalInstance) {
modalInstance._isShown = true; modalInstance.hide();
} }
throw new Error(data.error || `Server Error (${response.status})`);
}
return data;
});
})
.then(data => {
console.log("✅ Contact Added:", data);
if (data.identifier && addContactTargetInputId) { // Reset the form and clear errors after success
let targetInput = document.getElementById(addContactTargetInputId); form.reset();
if (targetInput) { addContactTargetInputId = null;
targetInput.value = data.identifier; document.getElementById("addContactError").classList.add("d-none");
} }
} })
.catch(error => {
console.error("❌ Error adding contact:", error.message);
showErrorMessage(error.message);
// ✅ Only close modal if form submission is successful refreshCsrfToken();
if (data.identifier) {
// ❌ Prevent modal from closing on error
let modalElement = document.getElementById("addContactModal"); let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement); let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) { if (modalInstance) {
modalInstance.hide(); modalInstance._isShown = true; // Keep modal open
} }
});
// Reset the form and clear errors after success
form.reset();
addContactTargetInputId = null;
document.getElementById("addContactError").classList.add("d-none");
}
})
.catch(error => {
console.error("❌ Error adding contact:", error.message);
showErrorMessage(error.message);
// ❌ Prevent modal from closing on error
let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) {
modalInstance._isShown = true; // Keep modal open
}
}); });
});
// Function to show error messages inside the modal // Function to show error messages inside the modal
function showErrorMessage(message) { function showErrorMessage(message) {
let errorContainer = document.getElementById("addContactError"); let errorContainer = document.getElementById("addContactError");
let submitButton = document.querySelector("#addContactModal button[type='submit']"); let submitButton = document.querySelector("#addContactModal button[type='submit']");
if (!errorContainer) {
let form = document.getElementById("addContactForm");
errorContainer = document.createElement("div");
errorContainer.id = "addContactError";
errorContainer.className = "alert alert-danger mt-2";
form.prepend(errorContainer);
}
errorContainer.innerText = message;
errorContainer.classList.remove("d-none"); // Ensure it's visible
// 🔹 Scroll to top of modal when error occurs
let modalBody = document.querySelector("#addContactModal .modal-body");
if (modalBody) {
modalBody.scrollTop = 0;
}
if (!errorContainer) {
let form = document.getElementById("addContactForm");
errorContainer = document.createElement("div");
errorContainer.id = "addContactError";
errorContainer.className = "alert alert-danger mt-2";
form.prepend(errorContainer);
} }
errorContainer.innerText = message;
errorContainer.classList.remove("d-none"); // Ensure it's visible
// 🔹 Scroll to top of modal when error occurs
let modalBody = document.querySelector("#addContactModal .modal-body");
if (modalBody) {
modalBody.scrollTop = 0;
}
// 🔹 Disable submit button on error
if (submitButton) {
submitButton.disabled = true;
}
}
let targetInputId = null; let targetInputId = null;
// Ensure target input is set correctly before the modal opens // Ensure target input is set correctly before the modal opens
@ -762,10 +756,80 @@ function showErrorMessage(message) {
targetInputId = null; targetInputId = null;
}); });
function generateAuthInfoC() {
const length = 16;
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let retVal = "";
let digitCount = 0;
// Generate initial random string
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * charset.length);
const char = charset.charAt(randomIndex);
retVal += char;
if (char >= '0' && char <= '9') {
digitCount++;
}
}
// Ensure there are at least two digits in the string
while (digitCount < 2) {
// Replace a non-digit character at a random position with a digit
const replacePosition = Math.floor(Math.random() * length);
if (!(retVal[replacePosition] >= '0' && retVal[replacePosition] <= '9')) {
const randomDigit = Math.floor(Math.random() * 10); // Generate a digit from 0 to 9
retVal = retVal.substring(0, replacePosition) + randomDigit + retVal.substring(replacePosition + 1);
digitCount++;
}
}
return retVal;
}
async function refreshCsrfToken() {
try {
let response = await fetch('/token-well', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({})
});
if (!response.ok) {
console.error("Failed to fetch CSRF token:", response.status);
return;
}
let data = await response.json();
let csrfNameField = document.querySelector('#addContactModal input[name="csrf_name"]');
let csrfValueField = document.querySelector('#addContactModal input[name="csrf_value"]');
if (csrfNameField && csrfValueField) {
csrfNameField.value = Object.keys(data)[0]; // Set the token name
csrfValueField.value = Object.values(data)[0]; // Set the token value
}
} catch (error) {
console.error("Error refreshing CSRF token:", error);
}
}
document.getElementById("addContactModal").addEventListener("show.bs.modal", function () {
document.getElementById('contactid').value = generateAuthInfoC();
document.getElementById('authInfoc').value = generateAuthInfoC();
});
var table; var table;
document.addEventListener("DOMContentLoaded", function(){ document.addEventListener("DOMContentLoaded", function(){
// Only refresh CSRF token when opening the "Add Contact" modal
let addContactModal = document.getElementById("addContactModal");
if (addContactModal) {
addContactModal.addEventListener("show.bs.modal", refreshCsrfToken);
}
const toggleLocCheckbox = document.getElementById('toggleLoc'); const toggleLocCheckbox = document.getElementById('toggleLoc');
const localizedSection = document.getElementById('localizedInfo'); const localizedSection = document.getElementById('localizedInfo');
@ -785,36 +849,6 @@ function showErrorMessage(message) {
const authInfoInput = document.getElementById('authInfoc'); const authInfoInput = document.getElementById('authInfoc');
authInfoInput.value = generateAuthInfoC(); authInfoInput.value = generateAuthInfoC();
function generateAuthInfoC() {
const length = 16;
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let retVal = "";
let digitCount = 0;
// Generate initial random string
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * charset.length);
const char = charset.charAt(randomIndex);
retVal += char;
if (char >= '0' && char <= '9') {
digitCount++;
}
}
// Ensure there are at least two digits in the string
while (digitCount < 2) {
// Replace a non-digit character at a random position with a digit
const replacePosition = Math.floor(Math.random() * length);
if (!(retVal[replacePosition] >= '0' && retVal[replacePosition] <= '9')) {
const randomDigit = Math.floor(Math.random() * 10); // Generate a digit from 0 to 9
retVal = retVal.substring(0, replacePosition) + randomDigit + retVal.substring(replacePosition + 1);
digitCount++;
}
}
return retVal;
}
function contactLinkFormatter(cell){ function contactLinkFormatter(cell){
var value = cell.getValue(); var value = cell.getValue();
return `<a href="/contact/view/${value}" style="font-weight:bold;">${value}</a>`; return `<a href="/contact/view/${value}" style="font-weight:bold;">${value}</a>`;

View file

@ -643,114 +643,108 @@
<link href="/assets/css/tabulator.min.css" rel="stylesheet"> <link href="/assets/css/tabulator.min.css" rel="stylesheet">
<script src="/assets/js/tabulator.min.js" defer></script> <script src="/assets/js/tabulator.min.js" defer></script>
<script> <script>
document.getElementById("cancelButton").addEventListener("click", function () { let addContactTargetInputId = null;
location.reload();
});
let addContactTargetInputId = null; // Capture the target input when opening "Add Contact" modal
document.addEventListener("click", function (e) {
// Capture the target input when opening "Add Contact" modal let openBtn = e.target.closest('.open-add-modal');
document.addEventListener("click", function (e) { if (openBtn) {
let openBtn = e.target.closest('.open-add-modal'); addContactTargetInputId = openBtn.getAttribute('data-input');
if (openBtn) {
addContactTargetInputId = openBtn.getAttribute('data-input');
}
});
document.getElementById("addContactForm").addEventListener("submit", function (e) {
e.preventDefault(); // Prevent full-page reload
let form = this;
let formData = new FormData(form);
fetch("/contact/create-api", {
method: "POST",
body: formData,
headers: {
"Accept": "application/json"
} }
}) });
.then(response => {
return response.json().then(data => {
if (!response.ok) {
showErrorMessage(data.error || `Server Error (${response.status})`);
// Keep the modal open when there's an error document.getElementById("addContactForm").addEventListener("submit", function (e) {
e.preventDefault(); // Prevent full-page reload
let form = this;
let formData = new FormData(form);
fetch("/contact/create-api", {
method: "POST",
body: formData,
headers: {
"Accept": "application/json"
}
})
.then(response => {
return response.json().then(data => {
if (!response.ok) {
showErrorMessage(data.error || `Server Error (${response.status})`);
// Keep the modal open when there's an error
let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) {
modalInstance._isShown = true;
}
throw new Error(data.error || `Server Error (${response.status})`);
}
return data;
});
})
.then(data => {
console.log("✅ Contact Added:", data);
if (data.identifier && addContactTargetInputId) {
let targetInput = document.getElementById(addContactTargetInputId);
if (targetInput) {
targetInput.value = data.identifier;
}
}
// ✅ Only close modal if form submission is successful
if (data.identifier) {
let modalElement = document.getElementById("addContactModal"); let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement); let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) { if (modalInstance) {
modalInstance._isShown = true; modalInstance.hide();
} }
throw new Error(data.error || `Server Error (${response.status})`);
}
return data;
});
})
.then(data => {
console.log("✅ Contact Added:", data);
if (data.identifier && addContactTargetInputId) { // Reset the form and clear errors after success
let targetInput = document.getElementById(addContactTargetInputId); form.reset();
if (targetInput) { addContactTargetInputId = null;
targetInput.value = data.identifier; document.getElementById("addContactError").classList.add("d-none");
} }
} })
.catch(error => {
console.error("❌ Error adding contact:", error.message);
showErrorMessage(error.message);
// ✅ Only close modal if form submission is successful refreshCsrfToken();
if (data.identifier) {
// ❌ Prevent modal from closing on error
let modalElement = document.getElementById("addContactModal"); let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement); let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) { if (modalInstance) {
modalInstance.hide(); modalInstance._isShown = true; // Keep modal open
} }
});
// Reset the form and clear errors after success
form.reset();
addContactTargetInputId = null;
document.getElementById("addContactError").classList.add("d-none");
}
})
.catch(error => {
console.error("❌ Error adding contact:", error.message);
showErrorMessage(error.message);
// ❌ Prevent modal from closing on error
let modalElement = document.getElementById("addContactModal");
let modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) {
modalInstance._isShown = true; // Keep modal open
}
}); });
});
// Function to show error messages inside the modal // Function to show error messages inside the modal
function showErrorMessage(message) { function showErrorMessage(message) {
let errorContainer = document.getElementById("addContactError"); let errorContainer = document.getElementById("addContactError");
let submitButton = document.querySelector("#addContactModal button[type='submit']"); let submitButton = document.querySelector("#addContactModal button[type='submit']");
if (!errorContainer) {
let form = document.getElementById("addContactForm");
errorContainer = document.createElement("div");
errorContainer.id = "addContactError";
errorContainer.className = "alert alert-danger mt-2";
form.prepend(errorContainer);
}
errorContainer.innerText = message;
errorContainer.classList.remove("d-none"); // Ensure it's visible
// 🔹 Scroll to top of modal when error occurs
let modalBody = document.querySelector("#addContactModal .modal-body");
if (modalBody) {
modalBody.scrollTop = 0;
}
if (!errorContainer) {
let form = document.getElementById("addContactForm");
errorContainer = document.createElement("div");
errorContainer.id = "addContactError";
errorContainer.className = "alert alert-danger mt-2";
form.prepend(errorContainer);
} }
errorContainer.innerText = message;
errorContainer.classList.remove("d-none"); // Ensure it's visible
// 🔹 Scroll to top of modal when error occurs
let modalBody = document.querySelector("#addContactModal .modal-body");
if (modalBody) {
modalBody.scrollTop = 0;
}
// 🔹 Disable submit button on error
if (submitButton) {
submitButton.disabled = true;
}
}
let targetInputId = null; let targetInputId = null;
// Ensure target input is set correctly before the modal opens // Ensure target input is set correctly before the modal opens
@ -790,10 +784,80 @@ function showErrorMessage(message) {
targetInputId = null; targetInputId = null;
}); });
function generateAuthInfoC() {
const length = 16;
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let retVal = "";
let digitCount = 0;
// Generate initial random string
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * charset.length);
const char = charset.charAt(randomIndex);
retVal += char;
if (char >= '0' && char <= '9') {
digitCount++;
}
}
// Ensure there are at least two digits in the string
while (digitCount < 2) {
// Replace a non-digit character at a random position with a digit
const replacePosition = Math.floor(Math.random() * length);
if (!(retVal[replacePosition] >= '0' && retVal[replacePosition] <= '9')) {
const randomDigit = Math.floor(Math.random() * 10); // Generate a digit from 0 to 9
retVal = retVal.substring(0, replacePosition) + randomDigit + retVal.substring(replacePosition + 1);
digitCount++;
}
}
return retVal;
}
async function refreshCsrfToken() {
try {
let response = await fetch('/token-well', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({})
});
if (!response.ok) {
console.error("Failed to fetch CSRF token:", response.status);
return;
}
let data = await response.json();
let csrfNameField = document.querySelector('#addContactModal input[name="csrf_name"]');
let csrfValueField = document.querySelector('#addContactModal input[name="csrf_value"]');
if (csrfNameField && csrfValueField) {
csrfNameField.value = Object.keys(data)[0]; // Set the token name
csrfValueField.value = Object.values(data)[0]; // Set the token value
}
} catch (error) {
console.error("Error refreshing CSRF token:", error);
}
}
document.getElementById("addContactModal").addEventListener("show.bs.modal", function () {
document.getElementById('contactid').value = generateAuthInfoC();
document.getElementById('authInfoc').value = generateAuthInfoC();
});
var table; var table;
document.addEventListener("DOMContentLoaded", function(){ document.addEventListener("DOMContentLoaded", function(){
// Only refresh CSRF token when opening the "Add Contact" modal
let addContactModal = document.getElementById("addContactModal");
if (addContactModal) {
addContactModal.addEventListener("show.bs.modal", refreshCsrfToken);
}
const toggleLocCheckbox = document.getElementById('toggleLoc'); const toggleLocCheckbox = document.getElementById('toggleLoc');
const localizedSection = document.getElementById('localizedInfo'); const localizedSection = document.getElementById('localizedInfo');
@ -813,36 +877,6 @@ function showErrorMessage(message) {
const authInfoInput = document.getElementById('authInfoc'); const authInfoInput = document.getElementById('authInfoc');
authInfoInput.value = generateAuthInfoC(); authInfoInput.value = generateAuthInfoC();
function generateAuthInfoC() {
const length = 16;
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let retVal = "";
let digitCount = 0;
// Generate initial random string
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * charset.length);
const char = charset.charAt(randomIndex);
retVal += char;
if (char >= '0' && char <= '9') {
digitCount++;
}
}
// Ensure there are at least two digits in the string
while (digitCount < 2) {
// Replace a non-digit character at a random position with a digit
const replacePosition = Math.floor(Math.random() * length);
if (!(retVal[replacePosition] >= '0' && retVal[replacePosition] <= '9')) {
const randomDigit = Math.floor(Math.random() * 10); // Generate a digit from 0 to 9
retVal = retVal.substring(0, replacePosition) + randomDigit + retVal.substring(replacePosition + 1);
digitCount++;
}
}
return retVal;
}
function contactLinkFormatter(cell){ function contactLinkFormatter(cell){
var value = cell.getValue(); var value = cell.getValue();
return `<a href="/contact/view/${value}" style="font-weight:bold;">${value}</a>`; return `<a href="/contact/view/${value}" style="font-weight:bold;">${value}</a>`;

View file

@ -155,6 +155,7 @@ $app->group('', function ($route) {
$route->post('/profile/logout-everywhere', ProfileController::class . ':logoutEverywhereElse')->setName('profile.logout.everywhere'); $route->post('/profile/logout-everywhere', ProfileController::class . ':logoutEverywhereElse')->setName('profile.logout.everywhere');
$route->get('/webauthn/register/challenge', ProfileController::class . ':getRegistrationChallenge')->setName('webauthn.register.challenge'); $route->get('/webauthn/register/challenge', ProfileController::class . ':getRegistrationChallenge')->setName('webauthn.register.challenge');
$route->post('/webauthn/register/verify', ProfileController::class . ':verifyRegistration')->setName('webauthn.register.verify'); $route->post('/webauthn/register/verify', ProfileController::class . ':verifyRegistration')->setName('webauthn.register.verify');
$route->post('/token-well', ProfileController::class .':tokenWell')->setName('tokenWell');
$route->get('/mode', HomeController::class .':mode')->setName('mode'); $route->get('/mode', HomeController::class .':mode')->setName('mode');
$route->get('/lang', HomeController::class .':lang')->setName('lang'); $route->get('/lang', HomeController::class .':lang')->setName('lang');