mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-06-25 01:17:51 +02:00
Fix: Thymeleaf syntax (/*[[...]]*/)
(#2659)
# Description Please provide a summary of the changes, including relevant motivation and context. Closes #(issue_number) ## Checklist - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have performed a self-review of my own code - [ ] I have attached images of the change if it is UI based - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] If my code has heavily changed functionality I have updated relevant docs on [Stirling-PDFs doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) - [x] My changes generate no new warnings - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only)
This commit is contained in:
parent
b2da426cc1
commit
76cbf94fdc
@ -1,10 +1,10 @@
|
|||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
|
||||||
var cacheInputs = localStorage.getItem("cacheInputs") || "disabled";
|
var cacheInputs = localStorage.getItem("cacheInputs") || "disabled";
|
||||||
if (cacheInputs !== "enabled") {
|
if (cacheInputs !== "enabled") {
|
||||||
return; // Stop execution if caching is not enabled
|
return; // Stop execution if caching is not enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to generate a key based on the form's action attribute
|
// Function to generate a key based on the form's action attribute
|
||||||
function generateStorageKey(form) {
|
function generateStorageKey(form) {
|
||||||
const action = form.getAttribute('action');
|
const action = form.getAttribute('action');
|
||||||
@ -24,11 +24,11 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||||||
for (let i = 0; i < elements.length; i++) {
|
for (let i = 0; i < elements.length; i++) {
|
||||||
const element = elements[i];
|
const element = elements[i];
|
||||||
// Skip elements without names, passwords, files, hidden fields, and submit/reset buttons
|
// Skip elements without names, passwords, files, hidden fields, and submit/reset buttons
|
||||||
if (!element.name ||
|
if (!element.name ||
|
||||||
element.type === 'password' ||
|
element.type === 'password' ||
|
||||||
element.type === 'file' ||
|
element.type === 'file' ||
|
||||||
//element.type === 'hidden' ||
|
//element.type === 'hidden' ||
|
||||||
element.type === 'submit' ||
|
element.type === 'submit' ||
|
||||||
element.type === 'reset') {
|
element.type === 'reset') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
// Find all forms and add CSRF token
|
// Find all forms and add CSRF token
|
||||||
const forms = document.querySelectorAll('form');
|
const forms = document.querySelectorAll('form');
|
||||||
const csrfToken = decodeCsrfToken(getCsrfToken());
|
const csrfToken = decodeCsrfToken(getCsrfToken());
|
||||||
|
|
||||||
// Only proceed if we have a cookie-based token
|
// Only proceed if we have a cookie-based token
|
||||||
if (csrfToken) {
|
if (csrfToken) {
|
||||||
forms.forEach(form => {
|
forms.forEach(form => {
|
||||||
@ -34,4 +34,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
form.appendChild(csrfInput);
|
form.appendChild(csrfInput);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -102,4 +102,4 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
toggleDarkMode();
|
toggleDarkMode();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -4,7 +4,7 @@ window.fetchWithCsrf = async function(url, options = {}) {
|
|||||||
.split('; ')
|
.split('; ')
|
||||||
.find(row => row.startsWith('XSRF-TOKEN='))
|
.find(row => row.startsWith('XSRF-TOKEN='))
|
||||||
?.split('=')[1];
|
?.split('=')[1];
|
||||||
|
|
||||||
if (cookieValue) {
|
if (cookieValue) {
|
||||||
return cookieValue;
|
return cookieValue;
|
||||||
}
|
}
|
||||||
@ -14,10 +14,10 @@ window.fetchWithCsrf = async function(url, options = {}) {
|
|||||||
|
|
||||||
// Create a new options object to avoid modifying the passed object
|
// Create a new options object to avoid modifying the passed object
|
||||||
const fetchOptions = { ...options };
|
const fetchOptions = { ...options };
|
||||||
|
|
||||||
// Ensure headers object exists
|
// Ensure headers object exists
|
||||||
fetchOptions.headers = { ...options.headers };
|
fetchOptions.headers = { ...options.headers };
|
||||||
|
|
||||||
// Add CSRF token if available
|
// Add CSRF token if available
|
||||||
const csrfToken = getCsrfToken();
|
const csrfToken = getCsrfToken();
|
||||||
if (csrfToken) {
|
if (csrfToken) {
|
||||||
@ -25,4 +25,4 @@ window.fetchWithCsrf = async function(url, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return fetch(url, fetchOptions);
|
return fetch(url, fetchOptions);
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,8 @@ function initializeGame() {
|
|||||||
const BASE_SPAWN_INTERVAL_MS = 1250; // milliseconds before a new enemy spawns
|
const BASE_SPAWN_INTERVAL_MS = 1250; // milliseconds before a new enemy spawns
|
||||||
const LEVEL_INCREASE_FACTOR_MS = 25; // milliseconds to decrease the spawn interval per level
|
const LEVEL_INCREASE_FACTOR_MS = 25; // milliseconds to decrease the spawn interval per level
|
||||||
const MAX_SPAWN_RATE_REDUCTION_MS = 800; // Max milliseconds from the base spawn interval
|
const MAX_SPAWN_RATE_REDUCTION_MS = 800; // Max milliseconds from the base spawn interval
|
||||||
|
|
||||||
|
|
||||||
let keysPressed = {};
|
let keysPressed = {};
|
||||||
const pdfs = [];
|
const pdfs = [];
|
||||||
const projectiles = [];
|
const projectiles = [];
|
||||||
@ -42,7 +42,7 @@ function initializeGame() {
|
|||||||
let pdfSpeed = BASE_PDF_SPEED;
|
let pdfSpeed = BASE_PDF_SPEED;
|
||||||
let gameOver = false;
|
let gameOver = false;
|
||||||
|
|
||||||
|
|
||||||
function handleKeys() {
|
function handleKeys() {
|
||||||
if (keysPressed["ArrowLeft"]) {
|
if (keysPressed["ArrowLeft"]) {
|
||||||
playerX -= PLAYER_MOVE_SPEED;
|
playerX -= PLAYER_MOVE_SPEED;
|
||||||
@ -72,7 +72,7 @@ function initializeGame() {
|
|||||||
function onKeyUp(event) {
|
function onKeyUp(event) {
|
||||||
keysPressed[event.key] = false;
|
keysPressed[event.key] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.removeEventListener("keydown", onKeydown);
|
document.removeEventListener("keydown", onKeydown);
|
||||||
document.removeEventListener("keyup", onKeyUp);
|
document.removeEventListener("keyup", onKeyUp);
|
||||||
document.addEventListener("keydown", onKeydown);
|
document.addEventListener("keydown", onKeydown);
|
||||||
@ -243,7 +243,7 @@ function initializeGame() {
|
|||||||
|
|
||||||
let spawnPdfTimeout;
|
let spawnPdfTimeout;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function spawnPdfInterval() {
|
function spawnPdfInterval() {
|
||||||
if (gameOver || paused) {
|
if (gameOver || paused) {
|
||||||
|
@ -39,4 +39,3 @@ document.getElementById("cacheInputs").addEventListener("change", function () {
|
|||||||
cacheInputs = this.checked ? "enabled" : "disabled";
|
cacheInputs = this.checked ? "enabled" : "disabled";
|
||||||
localStorage.setItem("cacheInputs", cacheInputs);
|
localStorage.setItem("cacheInputs", cacheInputs);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -308,37 +308,36 @@
|
|||||||
|
|
||||||
document.getElementById('syncToAccount').addEventListener('click', async function() {
|
document.getElementById('syncToAccount').addEventListener('click', async function() {
|
||||||
/*<![CDATA[*/
|
/*<![CDATA[*/
|
||||||
const urlUpdateUserSettings = /*[[@{/api/v1/user/updateUserSettings}]]*/ "/api/v1/user/updateUserSettings";
|
const urlUpdateUserSettings = /*[[@{/api/v1/user/updateUserSettings}]]*/ "/api/v1/user/updateUserSettings";
|
||||||
/*]]>*/
|
/*]]>*/
|
||||||
|
|
||||||
let settings = {};
|
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
|
||||||
const key = localStorage.key(i);
|
|
||||||
if(key !== 'debug' && key !== '0' && key !== '1' && !key.includes('pdfjs') && !key.includes('posthog') && !key.includes('pageViews')) {
|
|
||||||
settings[key] = localStorage.getItem(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await window.fetchWithCsrf(urlUpdateUserSettings, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(settings)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
location.reload();
|
|
||||||
} else {
|
|
||||||
alert('Error syncing settings to account');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error:', error);
|
|
||||||
alert('Error syncing settings to account');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
let settings = {};
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
const key = localStorage.key(i);
|
||||||
|
if(key !== 'debug' && key !== '0' && key !== '1' && !key.includes('pdfjs') && !key.includes('posthog') && !key.includes('pageViews')) {
|
||||||
|
settings[key] = localStorage.getItem(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await window.fetchWithCsrf(urlUpdateUserSettings, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(settings)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
alert('Error syncing settings to account');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
alert('Error syncing settings to account');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<div class="mb-3 mt-4 text-center">
|
<div class="mb-3 mt-4 text-center">
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
<!-- powered by section -->
|
<!-- powered by section -->
|
||||||
<div class="footer-powered-by">
|
<div class="footer-powered-by">
|
||||||
<span th:text="#{poweredBy}"></span>
|
<span th:text="#{poweredBy}"></span>
|
||||||
<a href="https://stirlingpdf.com" class="stirling-link">Stirling PDF</a>
|
<a href="https://stirlingpdf.com" class="stirling-link">Stirling PDF</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
@ -12,87 +12,87 @@
|
|||||||
<div class="container-flex">
|
<div class="container-flex">
|
||||||
<main class="form-signin">
|
<main class="form-signin">
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
const redirectAttempts = parseInt(localStorage.getItem('ssoRedirectAttempts') || '0');
|
const redirectAttempts = parseInt(localStorage.getItem('ssoRedirectAttempts') || '0');
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const hasRedirectError = urlParams.has('error');
|
const hasRedirectError = urlParams.has('error');
|
||||||
const hasLogout = urlParams.has('logout');
|
const hasLogout = urlParams.has('logout');
|
||||||
const hasMessage = urlParams.has('message');
|
const hasMessage = urlParams.has('message');
|
||||||
const MAX_REDIRECT_ATTEMPTS = 3;
|
const MAX_REDIRECT_ATTEMPTS = 3;
|
||||||
|
|
||||||
document.addEventListener('modeChanged', function(e) {
|
document.addEventListener('modeChanged', function(e) {
|
||||||
var mode = e.detail;
|
var mode = e.detail;
|
||||||
|
|
||||||
document.body.classList.remove("light-mode", "dark-mode", "rainbow-mode"); // remove all mode classes first
|
document.body.classList.remove("light-mode", "dark-mode", "rainbow-mode"); // remove all mode classes first
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "on":
|
case "on":
|
||||||
document.body.classList.add("dark-mode");
|
document.body.classList.add("dark-mode");
|
||||||
break;
|
break;
|
||||||
case "off":
|
case "off":
|
||||||
document.body.classList.add("light-mode");
|
document.body.classList.add("light-mode");
|
||||||
break;
|
break;
|
||||||
case "rainbow":
|
case "rainbow":
|
||||||
document.body.classList.add("rainbow-mode");
|
document.body.classList.add("rainbow-mode");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
const runningEE = [[${@runningEE}]];
|
const runningEE = /*[[${@runningEE}]]*/ false;
|
||||||
const SSOAutoLogin = [[${@SSOAutoLogin}]];
|
const SSOAutoLogin = /*[[${@SSOAutoLogin}]]*/ false;
|
||||||
const loginMethod = [[${loginMethod}]];
|
const loginMethod = /*[[${loginMethod}]]*/ 'normal';
|
||||||
const providerList = [[${providerlist}]];
|
const providerList = /*[[${providerlist}]]*/ {};
|
||||||
const shouldAutoRedirect = !hasRedirectError &&
|
const shouldAutoRedirect = !hasRedirectError &&
|
||||||
!hasLogout &&
|
!hasLogout &&
|
||||||
!hasMessage &&
|
!hasMessage &&
|
||||||
redirectAttempts < MAX_REDIRECT_ATTEMPTS &&
|
redirectAttempts < MAX_REDIRECT_ATTEMPTS &&
|
||||||
loginMethod !== 'normal' && runningEE && SSOAutoLogin;
|
loginMethod !== 'normal' && runningEE && SSOAutoLogin;
|
||||||
|
|
||||||
console.log('Should redirect:', shouldAutoRedirect, {
|
console.log('Should redirect:', shouldAutoRedirect, {
|
||||||
'No error': !hasRedirectError,
|
'No error': !hasRedirectError,
|
||||||
'No logout': !hasLogout,
|
'No logout': !hasLogout,
|
||||||
'No message': !hasMessage,
|
'No message': !hasMessage,
|
||||||
'Under max attempts': redirectAttempts < MAX_REDIRECT_ATTEMPTS,
|
'Under max attempts': redirectAttempts < MAX_REDIRECT_ATTEMPTS,
|
||||||
'Is OAuth2': loginMethod === 'oauth2'
|
'Is only OAuth2': loginMethod === 'oauth2'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (shouldAutoRedirect && providerList && Object.keys(providerList).length > 0) {
|
if (shouldAutoRedirect && providerList && Object.keys(providerList).length > 0) {
|
||||||
localStorage.setItem('ssoRedirectAttempts', redirectAttempts + 1);
|
localStorage.setItem('ssoRedirectAttempts', redirectAttempts + 1);
|
||||||
const firstProvider = Object.keys(providerList)[0];
|
const firstProvider = Object.keys(providerList)[0];
|
||||||
window.location.href = firstProvider;
|
window.location.href = firstProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset redirect attempts if successful login or after 1 hour
|
// Reset redirect attempts if successful login or after 1 hour
|
||||||
const lastAttemptTime = parseInt(localStorage.getItem('lastRedirectAttempt') || '0');
|
const lastAttemptTime = parseInt(localStorage.getItem('lastRedirectAttempt') || '0');
|
||||||
if (Date.now() - lastAttemptTime > 3600000) { // 1 hour
|
if (Date.now() - lastAttemptTime > 3600000) { // 1 hour
|
||||||
localStorage.setItem('ssoRedirectAttempts', '0');
|
localStorage.setItem('ssoRedirectAttempts', '0');
|
||||||
}
|
}
|
||||||
localStorage.setItem('lastRedirectAttempt', Date.now().toString());
|
localStorage.setItem('lastRedirectAttempt', Date.now().toString());
|
||||||
|
|
||||||
|
|
||||||
const defaultLocale = getStoredOrDefaultLocale();
|
const defaultLocale = getStoredOrDefaultLocale();
|
||||||
checkUserLanguage(defaultLocale);
|
checkUserLanguage(defaultLocale);
|
||||||
|
|
||||||
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
|
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
|
||||||
let activeItem;
|
let activeItem;
|
||||||
|
|
||||||
for (let i = 0; i < dropdownItems.length; i++) {
|
for (let i = 0; i < dropdownItems.length; i++) {
|
||||||
const item = dropdownItems[i];
|
const item = dropdownItems[i];
|
||||||
item.classList.remove('active');
|
item.classList.remove('active');
|
||||||
if (item.dataset.bsLanguageCode === defaultLocale) {
|
if (item.dataset.bsLanguageCode === defaultLocale) {
|
||||||
item.classList.add('active');
|
item.classList.add('active');
|
||||||
activeItem = item;
|
activeItem = item;
|
||||||
}
|
}
|
||||||
item.addEventListener('click', handleDropdownItemClick);
|
item.addEventListener('click', handleDropdownItemClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dropdown = document.getElementById('languageDropdown');
|
const dropdown = document.getElementById('languageDropdown');
|
||||||
|
|
||||||
if (activeItem) {
|
if (activeItem) {
|
||||||
dropdown.innerHTML = activeItem.innerHTML; // This will set the dropdown button's content to the active language's flag and name
|
dropdown.innerHTML = activeItem.innerHTML; // This will set the dropdown button's content to the active language's flag and name
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<img class="my-4" th:src="@{'/favicon.svg'}" alt="favicon" width="144" height="144">
|
<img class="my-4" th:src="@{'/favicon.svg'}" alt="favicon" width="144" height="144">
|
||||||
|
@ -16,15 +16,15 @@
|
|||||||
<span class="material-symbols-rounded tool-header-icon history">update</span>
|
<span class="material-symbols-rounded tool-header-icon history">update</span>
|
||||||
<span class="tool-header-text" th:text="#{releases.header}">Release Notes</span>
|
<span class="tool-header-text" th:text="#{releases.header}">Release Notes</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert alert-info" role="alert">
|
<div class="alert alert-info" role="alert">
|
||||||
<strong th:text="#{releases.current.version}">Current Installed Version</strong>:
|
<strong th:text="#{releases.current.version}">Current Installed Version</strong>:
|
||||||
<span id="currentVersion" th:text="${@appVersion}"></span>
|
<span id="currentVersion" th:text="${@appVersion}"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert alert-warning" role="alert">
|
<div class="alert alert-warning" role="alert">
|
||||||
<span th:text="#{releases.note}">All release notes are only available in English</span>
|
<span th:text="#{releases.note}">All release notes are only available in English</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="loading" class="text-center my-4">
|
<div id="loading" class="text-center my-4">
|
||||||
<div class="spinner-border text-primary" role="status">
|
<div class="spinner-border text-primary" role="status">
|
||||||
@ -51,42 +51,42 @@
|
|||||||
.release-notes-container {
|
.release-notes-container {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.release-card {
|
.release-card {
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #dee2e6;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.release-card.current-version {
|
.release-card.current-version {
|
||||||
border-color: #28a745;
|
border-color: #28a745;
|
||||||
background-color: rgba(40, 167, 69, 0.05);
|
background-color: rgba(40, 167, 69, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.release-header {
|
.release-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.release-header h3 {
|
.release-header h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version {
|
.version {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.release-date {
|
.release-date {
|
||||||
color: #6c757d;
|
color: #6c757d;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.release-body {
|
.release-body {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
@ -105,17 +105,17 @@
|
|||||||
|
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
/*<![CDATA[*/
|
/*<![CDATA[*/
|
||||||
|
|
||||||
// Get the current version from the appVersion bean
|
// Get the current version from the appVersion bean
|
||||||
const appVersion = [[${@appVersion}]];
|
const appVersion = /*[[${@appVersion}]]*/ '';
|
||||||
|
|
||||||
// GitHub API configuration
|
// GitHub API configuration
|
||||||
const REPO_OWNER = 'Stirling-Tools';
|
const REPO_OWNER = 'Stirling-Tools';
|
||||||
const REPO_NAME = 'Stirling-PDF';
|
const REPO_NAME = 'Stirling-PDF';
|
||||||
const GITHUB_API = 'https://api.github.com/repos/' + REPO_OWNER + '/' + REPO_NAME;
|
const GITHUB_API = 'https://api.github.com/repos/' + REPO_OWNER + '/' + REPO_NAME;
|
||||||
const GITHUB_URL = 'https://github.com/' + REPO_OWNER + '/' + REPO_NAME;
|
const GITHUB_URL = 'https://github.com/' + REPO_OWNER + '/' + REPO_NAME;
|
||||||
const MAX_RELEASES = 8;
|
const MAX_RELEASES = 8;
|
||||||
|
|
||||||
// Secure element creation helper
|
// Secure element creation helper
|
||||||
function createElement(tag, attributes = {}, children = []) {
|
function createElement(tag, attributes = {}, children = []) {
|
||||||
const element = document.createElement(tag);
|
const element = document.createElement(tag);
|
||||||
@ -134,7 +134,6 @@
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const ALLOWED_TAGS = {
|
const ALLOWED_TAGS = {
|
||||||
'a': ['href', 'target', 'rel', 'class'],
|
'a': ['href', 'target', 'rel', 'class'],
|
||||||
'img': ['src', 'alt', 'width', 'height', 'style'],
|
'img': ['src', 'alt', 'width', 'height', 'style'],
|
||||||
@ -149,7 +148,7 @@
|
|||||||
try {
|
try {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(htmlString, 'text/html');
|
const doc = parser.parseFromString(htmlString, 'text/html');
|
||||||
|
|
||||||
function sanitizeNode(node) {
|
function sanitizeNode(node) {
|
||||||
// Safety check for null/undefined
|
// Safety check for null/undefined
|
||||||
if (!node) return null;
|
if (!node) return null;
|
||||||
@ -162,7 +161,7 @@
|
|||||||
// Handle element nodes
|
// Handle element nodes
|
||||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
const tagName = node.tagName.toLowerCase();
|
const tagName = node.tagName.toLowerCase();
|
||||||
|
|
||||||
// Check if tag is allowed
|
// Check if tag is allowed
|
||||||
if (!ALLOWED_TAGS[tagName]) {
|
if (!ALLOWED_TAGS[tagName]) {
|
||||||
return document.createTextNode(node.textContent);
|
return document.createTextNode(node.textContent);
|
||||||
@ -170,7 +169,7 @@
|
|||||||
|
|
||||||
// Create new element
|
// Create new element
|
||||||
const cleanElement = document.createElement(tagName);
|
const cleanElement = document.createElement(tagName);
|
||||||
|
|
||||||
// Copy allowed attributes
|
// Copy allowed attributes
|
||||||
const allowedAttributes = ALLOWED_TAGS[tagName];
|
const allowedAttributes = ALLOWED_TAGS[tagName];
|
||||||
Array.from(node.attributes).forEach(attr => {
|
Array.from(node.attributes).forEach(attr => {
|
||||||
@ -244,7 +243,7 @@
|
|||||||
let lastIndex = 0;
|
let lastIndex = 0;
|
||||||
const result = document.createElement('span');
|
const result = document.createElement('span');
|
||||||
const urlRegex = new RegExp('https://github\\.com/' + REPO_OWNER + '/' + REPO_NAME + '/(?:issues|pull)/(\\d+)', 'g');
|
const urlRegex = new RegExp('https://github\\.com/' + REPO_OWNER + '/' + REPO_NAME + '/(?:issues|pull)/(\\d+)', 'g');
|
||||||
|
|
||||||
while ((match = urlRegex.exec(text)) !== null) {
|
while ((match = urlRegex.exec(text)) !== null) {
|
||||||
// Add text before the match
|
// Add text before the match
|
||||||
if (match.index > lastIndex) {
|
if (match.index > lastIndex) {
|
||||||
@ -259,7 +258,7 @@
|
|||||||
link.target = '_blank';
|
link.target = '_blank';
|
||||||
link.rel = 'noopener noreferrer';
|
link.rel = 'noopener noreferrer';
|
||||||
result.appendChild(link);
|
result.appendChild(link);
|
||||||
|
|
||||||
lastIndex = match.index + match[0].length;
|
lastIndex = match.index + match[0].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,15 +273,15 @@
|
|||||||
// Update formatText function to handle processGitHubReferences properly
|
// Update formatText function to handle processGitHubReferences properly
|
||||||
function formatText(text) {
|
function formatText(text) {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
|
|
||||||
// Split the text into lines
|
// Split the text into lines
|
||||||
const textWithoutComments = text.replace(/<!--[\s\S]*?-->/g, '');
|
const textWithoutComments = text.replace(/<!--[\s\S]*?-->/g, '');
|
||||||
const lines = textWithoutComments.split('\n');
|
const lines = textWithoutComments.split('\n');
|
||||||
let currentList = null;
|
let currentList = null;
|
||||||
|
|
||||||
lines.forEach(line => {
|
lines.forEach(line => {
|
||||||
const trimmedLine = line.trim();
|
const trimmedLine = line.trim();
|
||||||
|
|
||||||
// Skip empty lines but add spacing
|
// Skip empty lines but add spacing
|
||||||
if (!trimmedLine) {
|
if (!trimmedLine) {
|
||||||
if (currentList) {
|
if (currentList) {
|
||||||
@ -292,14 +291,14 @@ function formatText(text) {
|
|||||||
container.appendChild(document.createElement('br'));
|
container.appendChild(document.createElement('br'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the line is HTML
|
// Check if the line is HTML
|
||||||
if (trimmedLine.startsWith('<') && trimmedLine.endsWith('>')) {
|
if (trimmedLine.startsWith('<') && trimmedLine.endsWith('>')) {
|
||||||
if (currentList) {
|
if (currentList) {
|
||||||
container.appendChild(currentList);
|
container.appendChild(currentList);
|
||||||
currentList = null;
|
currentList = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const safeElement = createSafeElement(trimmedLine);
|
const safeElement = createSafeElement(trimmedLine);
|
||||||
if (safeElement) {
|
if (safeElement) {
|
||||||
container.appendChild(safeElement);
|
container.appendChild(safeElement);
|
||||||
@ -309,7 +308,7 @@ function formatText(text) {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for headers
|
// Check for headers
|
||||||
const headerMatch = trimmedLine.match(/^(#{1,3})\s+(.+)$/);
|
const headerMatch = trimmedLine.match(/^(#{1,3})\s+(.+)$/);
|
||||||
if (headerMatch) {
|
if (headerMatch) {
|
||||||
@ -326,40 +325,40 @@ function formatText(text) {
|
|||||||
container.appendChild(header);
|
container.appendChild(header);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for bullet points
|
// Check for bullet points
|
||||||
const bulletMatch = trimmedLine.match(/^[-*]\s+(.+)$/);
|
const bulletMatch = trimmedLine.match(/^[-*]\s+(.+)$/);
|
||||||
if (bulletMatch) {
|
if (bulletMatch) {
|
||||||
if (!currentList) {
|
if (!currentList) {
|
||||||
currentList = document.createElement('ul');
|
currentList = document.createElement('ul');
|
||||||
}
|
}
|
||||||
|
|
||||||
const listContent = bulletMatch[1];
|
const listContent = bulletMatch[1];
|
||||||
const listItem = document.createElement('li');
|
const listItem = document.createElement('li');
|
||||||
|
|
||||||
// Process GitHub references in list items
|
// Process GitHub references in list items
|
||||||
listItem.appendChild(processGitHubReferences(listContent));
|
listItem.appendChild(processGitHubReferences(listContent));
|
||||||
currentList.appendChild(listItem);
|
currentList.appendChild(listItem);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we reach here and have a list, append it
|
// If we reach here and have a list, append it
|
||||||
if (currentList) {
|
if (currentList) {
|
||||||
container.appendChild(currentList);
|
container.appendChild(currentList);
|
||||||
currentList = null;
|
currentList = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle regular paragraph
|
// Handle regular paragraph
|
||||||
const paragraph = document.createElement('p');
|
const paragraph = document.createElement('p');
|
||||||
paragraph.appendChild(processGitHubReferences(trimmedLine));
|
paragraph.appendChild(processGitHubReferences(trimmedLine));
|
||||||
container.appendChild(paragraph);
|
container.appendChild(paragraph);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Append any remaining list
|
// Append any remaining list
|
||||||
if (currentList) {
|
if (currentList) {
|
||||||
container.appendChild(currentList);
|
container.appendChild(currentList);
|
||||||
}
|
}
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,7 +367,7 @@ function compareVersions(v1, v2) {
|
|||||||
const normalize = v => v.replace(/^v/, '');
|
const normalize = v => v.replace(/^v/, '');
|
||||||
const v1Parts = normalize(v1).split('.').map(Number);
|
const v1Parts = normalize(v1).split('.').map(Number);
|
||||||
const v2Parts = normalize(v2).split('.').map(Number);
|
const v2Parts = normalize(v2).split('.').map(Number);
|
||||||
|
|
||||||
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
||||||
const v1Part = v1Parts[i] || 0;
|
const v1Part = v1Parts[i] || 0;
|
||||||
const v2Part = v2Parts[i] || 0;
|
const v2Part = v2Parts[i] || 0;
|
||||||
@ -386,7 +385,7 @@ async function loadReleases() {
|
|||||||
try {
|
try {
|
||||||
loading.classList.remove('d-none');
|
loading.classList.remove('d-none');
|
||||||
errorMessage.classList.add('d-none');
|
errorMessage.classList.add('d-none');
|
||||||
|
|
||||||
// Clear container safely
|
// Clear container safely
|
||||||
while (container.firstChild) {
|
while (container.firstChild) {
|
||||||
container.removeChild(container.firstChild);
|
container.removeChild(container.firstChild);
|
||||||
@ -400,8 +399,8 @@ async function loadReleases() {
|
|||||||
releases.sort((a, b) => compareVersions(b.tag_name, a.tag_name));
|
releases.sort((a, b) => compareVersions(b.tag_name, a.tag_name));
|
||||||
|
|
||||||
// Find index of current version
|
// Find index of current version
|
||||||
const currentVersionIndex = releases.findIndex(release =>
|
const currentVersionIndex = releases.findIndex(release =>
|
||||||
compareVersions(release.tag_name, 'v' + appVersion) === 0 ||
|
compareVersions(release.tag_name, 'v' + appVersion) === 0 ||
|
||||||
compareVersions(release.tag_name, appVersion) === 0
|
compareVersions(release.tag_name, appVersion) === 0
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -425,20 +424,20 @@ async function loadReleases() {
|
|||||||
|
|
||||||
relevantReleases.forEach((release, index) => {
|
relevantReleases.forEach((release, index) => {
|
||||||
const isCurrentVersion = index === 0; // First release in the array is current version
|
const isCurrentVersion = index === 0; // First release in the array is current version
|
||||||
|
|
||||||
const releaseCard = createElement('div', {
|
const releaseCard = createElement('div', {
|
||||||
class: `release-card ${isCurrentVersion ? 'current-version' : ''}`
|
class: `release-card ${isCurrentVersion ? 'current-version' : ''}`
|
||||||
});
|
});
|
||||||
|
|
||||||
const header = createElement('div', { class: 'release-header' });
|
const header = createElement('div', { class: 'release-header' });
|
||||||
|
|
||||||
const h3 = createElement('h3', {}, [
|
const h3 = createElement('h3', {}, [
|
||||||
createElement('span', { class: 'version' }, [release.tag_name]),
|
createElement('span', { class: 'version' }, [release.tag_name]),
|
||||||
createElement('span', { class: 'release-date' }, [
|
createElement('span', { class: 'release-date' }, [
|
||||||
new Date(release.created_at).toLocaleDateString()
|
new Date(release.created_at).toLocaleDateString()
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
header.appendChild(h3);
|
header.appendChild(h3);
|
||||||
|
|
||||||
if (isCurrentVersion) {
|
if (isCurrentVersion) {
|
||||||
@ -451,7 +450,7 @@ async function loadReleases() {
|
|||||||
|
|
||||||
const body = createElement('div', { class: 'release-body' });
|
const body = createElement('div', { class: 'release-body' });
|
||||||
body.appendChild(formatText(release.body || 'No release notes available.'));
|
body.appendChild(formatText(release.body || 'No release notes available.'));
|
||||||
|
|
||||||
releaseCard.appendChild(body);
|
releaseCard.appendChild(body);
|
||||||
container.appendChild(releaseCard);
|
container.appendChild(releaseCard);
|
||||||
});
|
});
|
||||||
@ -467,7 +466,7 @@ async function loadReleases() {
|
|||||||
|
|
||||||
// Load releases when the page loads
|
// Load releases when the page loads
|
||||||
document.addEventListener('DOMContentLoaded', loadReleases);
|
document.addEventListener('DOMContentLoaded', loadReleases);
|
||||||
|
|
||||||
/*]]>*/
|
/*]]>*/
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -21,9 +21,9 @@
|
|||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf')}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf')}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label th:text="#{validateSignature.selectCustomCert}" ></label>
|
<label th:text="#{validateSignature.selectCustomCert}" ></label>
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='certFile', multipleInputsForSingleRequest=false, notRequired=true, remoteCall='false', accept='.cer,.crt,.pem')}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='certFile', multipleInputsForSingleRequest=false, notRequired=true, remoteCall='false', accept='.cer,.crt,.pem')}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 text-left">
|
<div class="mb-3 text-left">
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{validateSignature.submit}"></button>
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{validateSignature.submit}"></button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,7 +29,7 @@ public class SPDFApplicationTest {
|
|||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ApplicationProperties applicationProperties;
|
private ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private SPDFApplication SPDFApplication;
|
private SPDFApplication SPDFApplication;
|
||||||
|
|
||||||
|
@ -103,4 +103,4 @@ class DatabaseConfigTest {
|
|||||||
|
|
||||||
assertThrows(UnsupportedProviderException.class, () -> databaseConfig.dataSource());
|
assertThrows(UnsupportedProviderException.class, () -> databaseConfig.dataSource());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,4 +98,4 @@ class RearrangePagesPDFControllerTest {
|
|||||||
assertNotNull(newPageOrder, "Returning null instead of page order list");
|
assertNotNull(newPageOrder, "Returning null instead of page order list");
|
||||||
assertEquals(Arrays.stream(expectedPageOrder.split(",")).map(Integer::parseInt).toList(), newPageOrder, "Page order doesn't match");
|
assertEquals(Arrays.stream(expectedPageOrder.split(",")).map(Integer::parseInt).toList(), newPageOrder, "Page order doesn't match");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user