wip - fixing static resource loading issue. only / missing

This commit is contained in:
Dario Ghunney Ware 2025-05-17 21:36:17 +01:00
parent b17e8fb8e4
commit d515c53b3c
149 changed files with 14134 additions and 770 deletions

View File

@ -1,65 +1,60 @@
Translation: Translation:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/resources/messages_*_*.properties' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/messages_*_*.properties'
- any-glob-to-any-file: 'scripts/ignore_translation.toml' - any-glob-to-any-file: 'scripts/ignore_translation.toml'
- any-glob-to-any-file: 'src/main/resources/templates/fragments/languages.html' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/templates/fragments/languages.html'
Front End: Front End:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/resources/templates/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/templates/**/*'
- any-glob-to-any-file: 'src/main/resources/static/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/static/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/controller/web/**' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/spdf/controller/web/**'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/UI/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/spdf/UI/**/*'
Java: Java:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/java/**/*.java' - any-glob-to-any-file: 'common/src/main/java/**/*.java'
- any-glob-to-any-file: 'proprietary/src/main/java/**/*.java'
- any-glob-to-any-file: 'stirling-pdf/src/main/java/**/*.java'
Back End: Back End:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/config/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/spdf/config/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/controller/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/spdf/controller/**/*'
- any-glob-to-any-file: 'src/main/resources/settings.yml.template' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/settings.yml.template'
- any-glob-to-any-file: 'src/main/resources/application.properties' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/application.properties'
- any-glob-to-any-file: 'src/main/resources/banner.txt' - any-glob-to-any-file: 'stirling-pdf/src/main/resources/banner.txt'
- any-glob-to-any-file: 'scripts/png_to_webp.py' - any-glob-to-any-file: 'scripts/png_to_webp.py'
- any-glob-to-any-file: 'split_photos.py' - any-glob-to-any-file: 'split_photos.py'
Security: Security:
- changed-files: - changed-files:
# todo: fix these # todo: fix these
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/config/interfaces/DatabaseInterface.java' - any-glob-to-any-file: 'src/main/java/stirling/software/spdf/config/security/**/*' # fixme
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/config/security/**/*' # todo: check if correct
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/controller/api/DatabaseController.java' - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/controller/api/EmailController.java' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/model/exception/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/controller/api/H2SQLController.java' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/controller/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/controller/web/AccountWebController.java' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/model/oauth2/provider/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/controller/web/DatabaseWebController.java' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseServiceInterface.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/controller/api/UserController.java' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseService.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/api/Email.java' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/model/api/Email.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/exception/BackupNotFoundException.java' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/model/AuthenticationType.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/exception/NoProviderFoundExceptionjava' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/model/ApiKeyAuthenticationToken.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/provider/**/*' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/model/AttemptCounter.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/AuthenticationType.java' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/model/Authority.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/ApiKeyAuthenticationToken.java' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/model/PersistentLogin.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/AttemptCounter.java' # - any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/model/SessionEntity.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/Authority.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/PersistentLogin.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/SessionEntity.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/config/security/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/provider/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/AuthenticationType.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/BackupNotFoundException.java'
- any-glob-to-any-file: 'scripts/download-security-jar.sh' - any-glob-to-any-file: 'scripts/download-security-jar.sh'
- any-glob-to-any-file: '.github/workflows/dependency-review.yml' - any-glob-to-any-file: '.github/workflows/dependency-review.yml'
- any-glob-to-any-file: '.github/workflows/scorecards.yml' - any-glob-to-any-file: '.github/workflows/scorecards.yml'
API: API:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/config/OpenApiConfig.java' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/spdf/config/OpenApiConfig.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/controller/web/MetricsController.java' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/spdf/controller/web/MetricsController.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/controller/api/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/spdf/controller/api/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/spdf/model/api/**/*' - any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/spdf/model/api/**/*'
- any-glob-to-any-file: 'scripts/png_to_webp.py' - any-glob-to-any-file: 'scripts/png_to_webp.py'
- any-glob-to-any-file: 'split_photos.py' - any-glob-to-any-file: 'split_photos.py'
- any-glob-to-any-file: '.github/workflows/swagger.yml' - any-glob-to-any-file: '.github/workflows/swagger.yml'
@ -93,7 +88,9 @@ Devtools:
Test: Test:
- changed-files: - changed-files:
- any-glob-to-any-file: 'cucumber/**/*' - any-glob-to-any-file: 'cucumber/**/*'
- any-glob-to-any-file: 'src/test/**/*' - any-glob-to-any-file: 'common/src/test/**/*'
- any-glob-to-any-file: 'proprietary/src/test/**/*'
- any-glob-to-any-file: 'stirling-pdf/src/test/**/*'
- any-glob-to-any-file: 'src/testing/**/*' - any-glob-to-any-file: 'src/testing/**/*'
- any-glob-to-any-file: '.pre-commit-config' - any-glob-to-any-file: '.pre-commit-config'
- any-glob-to-any-file: '.github/workflows/pre_commit.yml' - any-glob-to-any-file: '.github/workflows/pre_commit.yml'

View File

@ -42,9 +42,35 @@ bootJar {
enabled = false enabled = false
} }
sourceSets {
main {
java {
if (System.getenv('ADDITIONAL_FEATURES') == 'false') {
exclude 'stirling/software/proprietary/security/**'
}
if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') {
exclude 'stirling/software/spdf/UI/impl/**'
}
}
}
test {
java {
if (System.getenv('DOCKER_ENABLE_SECURITY') == 'false') {
exclude 'stirling/software/proprietary/security/**'
}
if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') {
exclude 'stirling/software/spdf/UI/impl/**'
}
}
}
}
allprojects { allprojects {
group = "stirling.software" group = 'stirling.software'
version = "0.46.1" version = '0.46.1'
afterEvaluate { afterEvaluate {
if (project == rootProject) return if (project == rootProject) return
@ -61,8 +87,6 @@ subprojects {
apply plugin: 'org.springframework.boot' apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management' apply plugin: 'io.spring.dependency-management'
java { java {
// 17 is lowest but we support and recommend 21 // 17 is lowest but we support and recommend 21
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_17
@ -74,7 +98,7 @@ subprojects {
configurations.configureEach { configurations.configureEach {
exclude group: 'commons-logging', module: 'commons-logging' exclude group: 'commons-logging', module: 'commons-logging'
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat" exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
// Exclude vulnerable BouncyCastle version used in tableau // Exclude vulnerable BouncyCastle version used in tableau
exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on' exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on'
exclude group: 'org.bouncycastle', module: 'bcutil-jdk15on' exclude group: 'org.bouncycastle', module: 'bcutil-jdk15on'
@ -121,45 +145,6 @@ licenseReport {
allowedLicensesFile = new File("$projectDir/allowed-licenses.json") allowedLicensesFile = new File("$projectDir/allowed-licenses.json")
} }
// todo: test if needed here
sourceSets {
main {
java {
if (System.getenv("ADDITIONAL_FEATURES") == "false") {
exclude 'stirling/software/proprietary/security/controller/**'
exclude 'stirling/software/proprietary/security/model/ApiKeyAuthenticationToken.java'
exclude 'stirling/software/proprietary/security/model/AttemptCounter.java'
exclude 'stirling/software/proprietary/security/model/Authority.java'
exclude 'stirling/software/proprietary/security/model/BackupNotFoundException.java'
exclude 'stirling/software/proprietary/security/model/PersistentLogin.java'
exclude 'stirling/software/proprietary/security/model/SessionEntity.java'
exclude 'stirling/software/proprietary/security/model/User.java'
exclude 'stirling/software/proprietary/security/database/repository/**'
}
if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") {
exclude "stirling/software/spdf/UI/impl/**"
}
}
}
test {
java {
if (System.getenv("DOCKER_ENABLE_SECURITY") == "false") {
exclude "stirling/software/spdf/config/security/**"
exclude "stirling/software/spdf/model/ApiKeyAuthenticationTokenTest.java"
exclude "stirling/software/spdf/controller/api/EmailControllerTest.java"
exclude "stirling/software/spdf/repository/**"
}
if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") {
exclude "stirling/software/spdf/UI/impl/**"
}
}
}
}
openApi { openApi {
apiDocsUrl = "http://localhost:8080/v1/api-docs" apiDocsUrl = "http://localhost:8080/v1/api-docs"
outputDir = file("$projectDir") outputDir = file("$projectDir")
@ -370,18 +355,18 @@ tasks.register('downloadTempJre') {
def jreArchive = new File(tmpDir, 'jre.tar.gz') def jreArchive = new File(tmpDir, 'jre.tar.gz')
def jreDir = new File(tmpDir, 'jre') def jreDir = new File(tmpDir, 'jre')
println "🔽 Downloading JRE to $jreArchive..." println "Downloading JRE to $jreArchive"
jreArchive.withOutputStream { out -> jreArchive.withOutputStream { out ->
new URI(jreUrl).toURL().withInputStream { from -> out << from } new URI(jreUrl).toURL().withInputStream { from -> out << from }
} }
println "📦 Extracting JRE to $jreDir..." println "Extracting JRE to $jreDir"
jreDir.mkdirs() jreDir.mkdirs()
providers.exec { providers.exec {
commandLine 'tar', '-xzf', jreArchive.absolutePath, '-C', jreDir.absolutePath, '--strip-components=1' commandLine 'tar', '-xzf', jreArchive.absolutePath, '-C', jreDir.absolutePath, '--strip-components=1'
}.result.get() }.result.get()
println "JRE ready at: $jreDir" println "JRE ready at: $jreDir"
ext.tempJrePath = jreDir.absolutePath ext.tempJrePath = jreDir.absolutePath
project.ext.tempJrePath = jreDir.absolutePath project.ext.tempJrePath = jreDir.absolutePath
} catch (Exception e) { } catch (Exception e) {

View File

@ -22,11 +22,11 @@ import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import stirling.software.common.model.exception.UnsupportedProviderException; import stirling.software.common.model.exception.UnsupportedProviderException;
import stirling.software.common.model.provider.GitHubProvider;
import stirling.software.common.model.provider.GoogleProvider;
import stirling.software.common.model.provider.KeycloakProvider;
import stirling.software.common.model.provider.Provider;
import stirling.software.common.util.ValidationUtil; import stirling.software.common.util.ValidationUtil;
import stirling.software.common.model.oauth2.provider.GitHubProvider;
import stirling.software.common.model.oauth2.provider.GoogleProvider;
import stirling.software.common.model.oauth2.provider.KeycloakProvider;
import stirling.software.common.model.oauth2.provider.Provider;
@Data @Data
@Component @Component

View File

@ -1,4 +1,4 @@
package stirling.software.spdf.model.api; package stirling.software.common.model.api;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;

View File

@ -0,0 +1,7 @@
package stirling.software.common.model.exception;
public class UnsupportedClaimException extends RuntimeException {
public UnsupportedClaimException(String message) {
super(message);
}
}

View File

@ -1,7 +0,0 @@
package stirling.software.common.model.exception;
public class UnsupportedUsernameAttribute extends RuntimeException {
public UnsupportedUsernameAttribute(String message) {
super(message);
}
}

View File

@ -1,4 +1,4 @@
package stirling.software.common.model.provider; package stirling.software.common.model.oauth2.provider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;

View File

@ -1,4 +1,4 @@
package stirling.software.common.model.provider; package stirling.software.common.model.oauth2.provider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;

View File

@ -1,4 +1,4 @@
package stirling.software.common.model.provider; package stirling.software.common.model.oauth2.provider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;

View File

@ -1,4 +1,4 @@
package stirling.software.common.model.provider; package stirling.software.common.model.oauth2.provider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -6,7 +6,7 @@ import java.util.Collection;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import stirling.software.common.model.enumeration.UsernameAttribute; import stirling.software.common.model.enumeration.UsernameAttribute;
import stirling.software.common.model.exception.UnsupportedUsernameAttribute; import stirling.software.common.model.exception.UnsupportedClaimException;
import static stirling.software.common.model.enumeration.UsernameAttribute.EMAIL; import static stirling.software.common.model.enumeration.UsernameAttribute.EMAIL;
@Data @Data
@ -80,7 +80,7 @@ public class Provider {
return usernameAttribute; return usernameAttribute;
} }
default -> default ->
throw new UnsupportedUsernameAttribute( throw new UnsupportedClaimException(
String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName)); String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName));
} }
} }
@ -91,7 +91,7 @@ public class Provider {
return usernameAttribute; return usernameAttribute;
} }
default -> default ->
throw new UnsupportedUsernameAttribute( throw new UnsupportedClaimException(
String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName)); String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName));
} }
} }
@ -102,7 +102,7 @@ public class Provider {
return usernameAttribute; return usernameAttribute;
} }
default -> default ->
throw new UnsupportedUsernameAttribute( throw new UnsupportedClaimException(
String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName)); String.format(EXCEPTION_MESSAGE, usernameAttribute, clientName));
} }
} }

View File

@ -1,6 +1,6 @@
package stirling.software.common.util; package stirling.software.common.util;
import stirling.software.common.model.provider.Provider; import stirling.software.common.model.oauth2.provider.Provider;
import static stirling.software.common.util.ValidationUtil.isCollectionEmpty; import static stirling.software.common.util.ValidationUtil.isCollectionEmpty;
import static stirling.software.common.util.ValidationUtil.isStringEmpty; import static stirling.software.common.util.ValidationUtil.isStringEmpty;

View File

@ -3,12 +3,10 @@ package stirling.software.common.util;
public class RequestUriUtil { public class RequestUriUtil {
public static boolean isStaticResource(String requestURI) { public static boolean isStaticResource(String requestURI) {
return isStaticResource("", requestURI); return isStaticResource("", requestURI);
} }
public static boolean isStaticResource(String contextPath, String requestURI) { public static boolean isStaticResource(String contextPath, String requestURI) {
return requestURI.startsWith(contextPath + "/css/") return requestURI.startsWith(contextPath + "/css/")
|| requestURI.startsWith(contextPath + "/fonts/") || requestURI.startsWith(contextPath + "/fonts/")
|| requestURI.startsWith(contextPath + "/js/") || requestURI.startsWith(contextPath + "/js/")

View File

@ -1,7 +1,6 @@
package stirling.software.common.util; package stirling.software.common.util;
import java.util.Collection; import java.util.Collection;
import stirling.software.common.model.provider.Provider;
public class ValidationUtil { public class ValidationUtil {

View File

@ -2,6 +2,7 @@ package stirling.software.common.util;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
@ -9,11 +10,9 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import stirling.software.common.model.enumeration.UsernameAttribute; import stirling.software.common.model.enumeration.UsernameAttribute;
import stirling.software.common.model.provider.GitHubProvider; import stirling.software.common.model.oauth2.provider.GitHubProvider;
import stirling.software.common.model.provider.GoogleProvider; import stirling.software.common.model.oauth2.provider.GoogleProvider;
import stirling.software.common.model.provider.Provider; import stirling.software.common.model.oauth2.provider.Provider;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@ -27,13 +26,13 @@ class ProviderUtilTest {
when(provider.getClientSecret()).thenReturn("clientSecret"); when(provider.getClientSecret()).thenReturn("clientSecret");
when(provider.getScopes()).thenReturn(List.of("read:user")); when(provider.getScopes()).thenReturn(List.of("read:user"));
assertTrue(ProviderUtil.validateProvider(provider)); Assertions.assertTrue(ProviderUtil.validateProvider(provider));
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("providerParams") @MethodSource("providerParams")
void testUnsuccessfulValidation(Provider provider) { void testUnsuccessfulValidation(Provider provider) {
assertFalse(ProviderUtil.validateProvider(provider)); Assertions.assertFalse(ProviderUtil.validateProvider(provider));
} }
public static Stream<Arguments> providerParams() { public static Stream<Arguments> providerParams() {

View File

@ -14,40 +14,27 @@ bootJar {
enabled = false enabled = false
} }
// todo: check if needed here
sourceSets { sourceSets {
main { main {
java { java {
if (System.getenv('ADDITIONAL_FEATURES') == 'false') { if (System.getenv('ADDITIONAL_FEATURES') == 'false') {
exclude 'stirling/software/enterprise/security/UserAuthenticationFilter.java' exclude 'stirling/software/proprietary/security/**'
exclude 'stirling/software/enterprise/security/UserBasedRateLimitingFilter.java' }
exclude 'stirling/software/enterprise/security/CustomAuthenticationSuccessHandler.java'
exclude 'stirling/software/enterprise/security/CustomLogoutSuccessHandler.java' if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') {
exclude 'stirling/software/enterprise/security/FirstLoginFilter.java' exclude 'stirling/software/spdf/UI/impl/**'
exclude 'stirling/software/enterprise/security/IPRateLimitingFilter.java'
exclude 'stirling/software/enterprise/security/RateLimitResetScheduler.java'
exclude 'stirling/software/enterprise/security/CustomAuthenticationFailureHandler.java'
exclude 'stirling/software/enterprise/security/InitialSecuritySetup.java'
exclude 'stirling/software/enterprise/security/configuration/**'
exclude 'stirling/software/enterprise/security/controller/**'
exclude 'stirling/software/enterprise/security/database/**'
exclude 'stirling/software/enterprise/security/oauth2/**'
exclude 'stirling/software/enterprise/security/saml2/**'
exclude 'stirling/software/enterprise/security/service/**'
exclude 'stirling/software/enterprise/security/session/**'
exclude 'stirling/software/enterprise/security/model/ApiKeyAuthenticationToken.java'
exclude 'stirling/software/enterprise/security/model/AttemptCounter.java'
exclude 'stirling/software/enterprise/security/model/Authority.java'
exclude 'stirling/software/enterprise/security/model/BackupNotFoundException.java'
exclude 'stirling/software/enterprise/security/model/PersistentLogin.java'
exclude 'stirling/software/enterprise/security/model/SessionEntity.java'
exclude 'stirling/software/enterprise/security/model/User.java'
} }
} }
test { test {
java { java {
if (System.getenv('ADDITIONAL_FEATURES') == 'false') { if (System.getenv('DOCKER_ENABLE_SECURITY') == 'false') {
exclude 'stirling/software/enterprise/security/**' exclude 'stirling/software/proprietary/security/**'
}
if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') {
exclude 'stirling/software/spdf/UI/impl/**'
} }
} }
} }
@ -60,31 +47,31 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jetty' implementation 'org.springframework.boot:spring-boot-starter-jetty'
implementation 'io.swagger.core.v3:swagger-core-jakarta:2.2.30' implementation 'io.swagger.core.v3:swagger-core-jakarta:2.2.30'
implementation 'com.bucket4j:bucket4j_jdk17-core:8.14.0' // https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17 implementation 'com.bucket4j:bucket4j_jdk17-core:8.14.0'
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
implementation 'org.bouncycastle:bcprov-jdk18on:1.80' implementation 'org.bouncycastle:bcprov-jdk18on:1.80'
if (System.getenv('ADDITIONAL_FEATURES') == 'true') { implementation 'org.springframework:spring-jdbc:6.2.6'
implementation 'org.springframework:spring-jdbc:6.2.6' implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation "org.springframework.security:spring-security-core:$springSecuritySamlVersion" implementation "org.springframework.security:spring-security-core:$springSecuritySamlVersion"
implementation "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion" implementation "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion"
implementation 'org.springframework.session:spring-session-core:3.4.3' implementation 'org.springframework.session:spring-session-core:3.4.3'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE'
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
runtimeOnly 'com.h2database:h2:2.3.232' // Don't upgrade h2database
runtimeOnly 'org.postgresql:postgresql:42.7.5'
constraints {
implementation "org.opensaml:opensaml-core:$openSamlVersion"
implementation "org.opensaml:opensaml-saml-api:$openSamlVersion"
implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion"
}
implementation 'com.coveo:saml-client:5.0.0'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE'
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
runtimeOnly 'com.h2database:h2:2.3.232' // Don't upgrade h2database
runtimeOnly 'org.postgresql:postgresql:42.7.5'
constraints {
implementation "org.opensaml:opensaml-core:$openSamlVersion"
implementation "org.opensaml:opensaml-saml-api:$openSamlVersion"
implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion"
} }
implementation 'com.coveo:saml-client:5.0.0'
compileOnly "org.projectlombok:lombok:$lombokVersion" compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion" annotationProcessor "org.projectlombok:lombok:$lombokVersion"

View File

@ -21,7 +21,7 @@ import stirling.software.common.configuration.AppConfig;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2; import stirling.software.common.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.common.model.ApplicationProperties.Security.SAML2; import stirling.software.common.model.ApplicationProperties.Security.SAML2;
import stirling.software.common.model.provider.KeycloakProvider; import stirling.software.common.model.oauth2.provider.KeycloakProvider;
import stirling.software.common.util.UrlUtils; import stirling.software.common.util.UrlUtils;
import stirling.software.proprietary.security.saml2.CertificateUtils; import stirling.software.proprietary.security.saml2.CertificateUtils;
import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticatedPrincipal; import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticatedPrincipal;

View File

@ -1,4 +1,4 @@
package stirling.software.SPDF.config.security.mail; package stirling.software.proprietary.security.configuration;
import java.util.Properties; import java.util.Properties;
@ -10,8 +10,7 @@ import org.springframework.mail.javamail.JavaMailSenderImpl;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties;
/** /**
* This configuration class provides the JavaMailSender bean, which is used to send emails. It reads * This configuration class provides the JavaMailSender bean, which is used to send emails. It reads

View File

@ -1,4 +1,4 @@
package stirling.software.SPDF.controller.api; package stirling.software.proprietary.security.controller.api;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -16,8 +16,8 @@ import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.mail.EmailService; import stirling.software.proprietary.security.model.api.Email;
import stirling.software.SPDF.model.api.Email; import stirling.software.proprietary.security.service.EmailService;
/** /**
* Controller for handling email-related API requests. This controller exposes an endpoint for * Controller for handling email-related API requests. This controller exposes an endpoint for

View File

@ -35,9 +35,9 @@ import stirling.software.common.model.ApplicationProperties.Security;
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2; import stirling.software.common.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2.Client; import stirling.software.common.model.ApplicationProperties.Security.OAUTH2.Client;
import stirling.software.common.model.ApplicationProperties.Security.SAML2; import stirling.software.common.model.ApplicationProperties.Security.SAML2;
import stirling.software.common.model.provider.GitHubProvider; import stirling.software.common.model.oauth2.provider.GitHubProvider;
import stirling.software.common.model.provider.GoogleProvider; import stirling.software.common.model.oauth2.provider.GoogleProvider;
import stirling.software.common.model.provider.KeycloakProvider; import stirling.software.common.model.oauth2.provider.KeycloakProvider;
import stirling.software.proprietary.security.database.repository.UserRepository; import stirling.software.proprietary.security.database.repository.UserRepository;
import stirling.software.proprietary.security.model.Authority; import stirling.software.proprietary.security.model.Authority;
import stirling.software.proprietary.security.model.SessionEntity; import stirling.software.proprietary.security.model.SessionEntity;

View File

@ -1,4 +1,4 @@
package stirling.software.SPDF.model.api; package stirling.software.proprietary.security.model.api;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import stirling.software.common.model.api.GeneralFile;
@Data @Data
@NoArgsConstructor @NoArgsConstructor

View File

@ -1,4 +1,4 @@
package stirling.software.common.model.exception; package stirling.software.proprietary.security.model.exception;
public class BackupNotFoundException extends RuntimeException { public class BackupNotFoundException extends RuntimeException {
public BackupNotFoundException(String message) { public BackupNotFoundException(String message) {

View File

@ -1,4 +1,4 @@
package stirling.software.common.model.exception; package stirling.software.proprietary.security.model.exception;
public class NoProviderFoundException extends Exception { public class NoProviderFoundException extends Exception {
public NoProviderFoundException(String message) { public NoProviderFoundException(String message) {

View File

@ -23,11 +23,11 @@ import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2; import stirling.software.common.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2.Client; import stirling.software.common.model.ApplicationProperties.Security.OAUTH2.Client;
import stirling.software.common.model.enumeration.UsernameAttribute; import stirling.software.common.model.enumeration.UsernameAttribute;
import stirling.software.common.model.exception.NoProviderFoundException; import stirling.software.proprietary.security.model.exception.NoProviderFoundException;
import stirling.software.common.model.provider.GitHubProvider; import stirling.software.common.model.oauth2.provider.GitHubProvider;
import stirling.software.common.model.provider.GoogleProvider; import stirling.software.common.model.oauth2.provider.GoogleProvider;
import stirling.software.common.model.provider.KeycloakProvider; import stirling.software.common.model.oauth2.provider.KeycloakProvider;
import stirling.software.common.model.provider.Provider; import stirling.software.common.model.oauth2.provider.Provider;
import stirling.software.proprietary.security.model.User; import stirling.software.proprietary.security.model.User;
import stirling.software.proprietary.security.service.UserService; import stirling.software.proprietary.security.service.UserService;
import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE; import static org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE;

View File

@ -26,7 +26,7 @@ import org.springframework.stereotype.Service;
import stirling.software.common.configuration.InstallationPathConfig; import stirling.software.common.configuration.InstallationPathConfig;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.model.FileInfo; import stirling.software.common.model.FileInfo;
import stirling.software.common.model.exception.BackupNotFoundException; import stirling.software.proprietary.security.model.exception.BackupNotFoundException;
@Slf4j @Slf4j
@Service @Service

View File

@ -1,4 +1,4 @@
package stirling.software.SPDF.config.security.mail; package stirling.software.proprietary.security.service;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSender;
@ -12,8 +12,8 @@ import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
import stirling.software.SPDF.model.api.Email; import stirling.software.proprietary.security.model.api.Email;
/** /**
* Service class responsible for sending emails, including those with attachments. It uses * Service class responsible for sending emails, including those with attachments. It uses

View File

@ -1,4 +1,4 @@
package stirling.software.SPDF.controller.api; package stirling.software.proprietary.security.controller.api;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@ -14,8 +14,8 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.config.security.mail.EmailService; import stirling.software.proprietary.security.model.api.Email;
import stirling.software.SPDF.model.api.Email; import stirling.software.proprietary.security.service.EmailService;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
public class EmailControllerTest { public class EmailControllerTest {

View File

@ -1,6 +1,4 @@
package stirling.software.SPDF.config.security.mail; package stirling.software.proprietary.security.service;
import static org.mockito.Mockito.*;
import jakarta.mail.MessagingException; import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage; import jakarta.mail.internet.MimeMessage;
@ -10,10 +8,12 @@ import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
import stirling.software.SPDF.model.api.Email; import stirling.software.proprietary.security.model.api.Email;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
public class EmailServiceTest { public class EmailServiceTest {

View File

@ -1,34 +0,0 @@
package stirling.software.SPDF.model.api.security;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.common.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class RedactPdfRequest extends PDFFile {
@Schema(
description = "List of text to redact from the PDF",
type = "string",
requiredMode = Schema.RequiredMode.REQUIRED)
private String listOfText;
@Schema(description = "Whether to use regex for the listOfText", defaultValue = "false")
private boolean useRegex;
@Schema(description = "Whether to use whole word search", defaultValue = "false")
private boolean wholeWordSearch;
@Schema(description = "Hexadecimal color code for redaction, e.g. #FF0000 or 000000", defaultValue = "#000000")
private String redactColor = "#000000";
@Schema(description = "Custom padding for redaction", type = "number")
private float customPadding;
@Schema(description = "Convert the redacted PDF to an image", defaultValue = "false")
private boolean convertPDFToImage;
}

196
stirling-pdf/.gitignore vendored Normal file
View File

@ -0,0 +1,196 @@
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.exe
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
.classpath
.project
version.properties
#### Stirling-PDF Files ###
pipeline/watchedFolders/
pipeline/finishedFolders/
customFiles/
configs/
watchedFolders/
clientWebUI/
!cucumber/
!cucumber/exampleFiles/
!cucumber/exampleFiles/example_html.zip
exampleYmlFiles/stirling/
/testing/file_snapshots
SwaggerDoc.json
# Gradle
.gradle
.lock
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
.apt_generated_test/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project
### Eclipse Patch ###
# Spring Boot Tooling
.sts4-cache/
### Git ###
# Created by git for backups. To disable backups in Git:
# $ git config --global mergetool.keepBackup false
*.orig
# Created by git when using merge tools for conflicts
*.BACKUP.*
*.BASE.*
*.LOCAL.*
*.REMOTE.*
*_BACKUP_*.txt
*_BASE_*.txt
*_LOCAL_*.txt
*_REMOTE_*.txt
### Java ###
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
*.db
/build
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*.pyo
# Virtual environments
.env*
.venv*
env*/
venv*/
ENV/
env.bak/
venv.bak/
# VS Code
/.vscode/**/*
!/.vscode/settings.json
!/.vscode/extensions.json
# IntelliJ IDEA
.idea/
*.iml
out/
# Ignore Mac DS_Store files
.DS_Store
**/.DS_Store
# cucumber
/cucumber/reports/**
# Certs and Security Files
*.p12
*.pk8
*.pem
*.crt
*.cer
*.cert
*.der
*.key
*.csr
*.kdbx
*.jks
*.asc
# SSH Keys
*.pub
*.priv
id_rsa
id_rsa.pub
id_ecdsa
id_ecdsa.pub
id_ed25519
id_ed25519.pub
.ssh/
*ssh
# cache
.cache
.ruff_cache
.mypy_cache
.pytest_cache
.ipynb_checkpoints
**/jcef-bundle/
# node_modules
node_modules/
*.mjs

View File

@ -40,7 +40,7 @@
</root> </root>
<!-- Specific Logger --> <!-- Specific Logger -->
<logger name="stirling.software.spdf.config.security.CustomAuthenticationFailureHandler" <logger name="stirling.software.SPDF.config.security.CustomAuthenticationFailureHandler"
level="ERROR" additivity="false"> level="ERROR" additivity="false">
<appender-ref ref="CONSOLE"/> <appender-ref ref="CONSOLE"/>
<appender-ref ref="AUTHLOG"/> <appender-ref ref="AUTHLOG"/>

View File

@ -86,7 +86,7 @@ mail:
from: '' # sender email address from: '' # sender email address
legal: legal:
termsAndConditions: https://www.stirlingpdf.com/terms # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder
accessibilityStatement: '' # URL to the accessibility statement of your application (e.g. https://example.com/accessibility). Empty string to disable or filename to load from local file in static folder accessibilityStatement: '' # URL to the accessibility statement of your application (e.g. https://example.com/accessibility). Empty string to disable or filename to load from local file in static folder
cookiePolicy: '' # URL to the cookie policy of your application (e.g. https://example.com/cookie). Empty string to disable or filename to load from local file in static folder cookiePolicy: '' # URL to the cookie policy of your application (e.g. https://example.com/cookie). Empty string to disable or filename to load from local file in static folder
@ -114,11 +114,11 @@ system:
name: postgres # set the name of your database. Should match the name of the database you create name: postgres # set the name of your database. Should match the name of the database you create
customPaths: customPaths:
pipeline: pipeline:
watchedFoldersDir: '' # Defaults to /pipeline/watchedFolders watchedFoldersDir: '' #Defaults to /pipeline/watchedFolders
finishedFoldersDir: '' # Defaults to /pipeline/finishedFolders finishedFoldersDir: '' #Defaults to /pipeline/finishedFolders
operations: operations:
weasyprint: '' # Defaults to /opt/venv/bin/weasyprint weasyprint: '' #Defaults to /opt/venv/bin/weasyprint
unoconvert: '' # Defaults to /opt/venv/bin/unoconvert unoconvert: '' #Defaults to /opt/venv/bin/unoconvert
fileUploadLimit: '' # Defaults to "". No limit when string is empty. Set a number, between 0 and 999, followed by one of the following strings to set a limit. "KB", "MB", "GB". fileUploadLimit: '' # Defaults to "". No limit when string is empty. Set a number, between 0 and 999, followed by one of the following strings to set a limit. "KB", "MB", "GB".
ui: ui:

View File

@ -1,240 +1,240 @@
<!DOCTYPE html> <!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" <html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
xmlns:th="https://www.thymeleaf.org"> xmlns:th="https://www.thymeleaf.org">
<head> <head>
<th:block th:insert="~{fragments/common :: head(title='')}"></th:block> <th:block th:insert="~{fragments/common :: head(title='')}"></th:block>
</head> </head>
<body> <body>
<div id="page-container"> <div id="page-container">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block> <th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<div style="transform-origin: top;" <div style="transform-origin: top;"
id="scale-wrap"> id="scale-wrap">
<br class="d-md-none"> <br class="d-md-none">
<!-- Features --> <!-- Features -->
<script th:src="@{'/js/homecard.js'}"></script> <script th:src="@{'/js/homecard.js'}"></script>
<div style=" <div style="
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column;" flex-direction: column;"
> >
<div> <div>
<br> <br>
<div style="justify-content: center; display: flex;"> <div style="justify-content: center; display: flex;">
<div style="margin:0 3rem"> <div style="margin:0 3rem">
<div> <div>
<div <div
style="display:flex; flex-direction: column; justify-content: center; width:100%; margin-bottom:1rem"> style="display:flex; flex-direction: column; justify-content: center; width:100%; margin-bottom:1rem">
<div style="width:fit-content; margin: 0 auto; padding: 0 3rem"> <div style="width:fit-content; margin: 0 auto; padding: 0 3rem">
<p class="lead fs-4" <p class="lead fs-4"
th:text="${@homeText != 'null' and @homeText != null and @homeText != ''} ? ${@homeText} : #{home.desc}"> th:text="${@homeText != 'null' and @homeText != null and @homeText != ''} ? ${@homeText} : #{home.desc}">
</p> </p>
</div>
<div id="groupRecent" style="width: fit-content; margin: 0 auto">
<div
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.recent})}">
</div> </div>
<div id="groupRecent" style="width: fit-content; margin: 0 auto"> <div class="recent-features">
<div <div class="newfeature"
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.recent})}"> th:insert="~{fragments/navbarEntryCustom :: navbarEntry('redact', '/images/redact-manual.svg#icon-redact-manual', 'home.redact.title', 'home.redact.desc', 'redact.tags', 'security')}">
</div> </div>
<div class="recent-features"> <div class="newfeature"
<div class="newfeature" th:insert="~{fragments/navbarEntry :: navbarEntry ('multi-tool', 'construction', 'home.multiTool.title', 'home.multiTool.desc', 'multiTool.tags', 'organize')}">
th:insert="~{fragments/navbarEntryCustom :: navbarEntry('redact', '/images/redact-manual.svg#icon-redact-manual', 'home.redact.title', 'home.redact.desc', 'redact.tags', 'security')}"> </div>
</div> <div class="newfeature"
<div class="newfeature" th:insert="~{fragments/navbarEntry :: navbarEntry('compress-pdf', 'zoom_in_map', 'home.compressPdfs.title', 'home.compressPdfs.desc', 'compressPDFs.tags', 'advance')}">
th:insert="~{fragments/navbarEntry :: navbarEntry ('multi-tool', 'construction', 'home.multiTool.title', 'home.multiTool.desc', 'multiTool.tags', 'organize')}">
</div>
<div class="newfeature"
th:insert="~{fragments/navbarEntry :: navbarEntry('compress-pdf', 'zoom_in_map', 'home.compressPdfs.title', 'home.compressPdfs.desc', 'compressPDFs.tags', 'advance')}">
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<span class="material-symbols-rounded search-icon">
</div>
<span class="material-symbols-rounded search-icon">
search search
</span> </span>
<input type="text" id="searchBar" onkeyup="filterCards()" th:placeholder="#{home.searchBar}" autofocus> <input type="text" id="searchBar" onkeyup="filterCards()" th:placeholder="#{home.searchBar}" autofocus>
<div style="display: flex; column-gap: 3rem; flex-wrap: wrap; margin-left:1rem"> <div style="display: flex; column-gap: 3rem; flex-wrap: wrap; margin-left:1rem">
<div <div
style="height:2.5rem; display: flex; align-items: center; cursor: pointer; justify-content: center;"> style="height:2.5rem; display: flex; align-items: center; cursor: pointer; justify-content: center;">
<label for="sort-options" th:text="#{home.sortBy}">Sort by:</label> <label for="sort-options" th:text="#{home.sortBy}">Sort by:</label>
<select id="sort-options" style="border:none;"> <select id="sort-options" style="border:none;">
<option value="alphabetical" th:text="#{home.alphabetical}"> </option> <option value="alphabetical" th:text="#{home.alphabetical}"> </option>
<!-- <option value="personal">Your most used</option> --> <!-- <option value="personal">Your most used</option> -->
<option value="global" th:text="#{home.globalPopularity}"></option> <option value="global" th:text="#{home.globalPopularity}"></option>
<!-- <option value="server">Popularity in organisation</option> --> <!-- <option value="server">Popularity in organisation</option> -->
</select> </select>
</div> </div>
<div <div
style="display: flex; align-items: center; flex-wrap: wrap; align-content: flex-start; width: fit-content; max-width: 100%; gap:2rem; justify-content: center;"> style="display: flex; align-items: center; flex-wrap: wrap; align-content: flex-start; width: fit-content; max-width: 100%; gap:2rem; justify-content: center;">
<div th:title="#{home.setFavorites}" style="display: flex; align-items: center; cursor: pointer;" <div th:title="#{home.setFavorites}" style="display: flex; align-items: center; cursor: pointer;"
onclick="toggleFavoritesMode()"> onclick="toggleFavoritesMode()">
<span class="material-symbols-rounded toggle-favourites" <span class="material-symbols-rounded toggle-favourites"
style="font-size: 2rem; margin-left: 0.2rem;"> style="font-size: 2rem; margin-left: 0.2rem;">
star star
</span> </span>
</div> </div>
<div onclick="toggleFavoritesView()" th:title="#{home.hideFavorites}" id="favouritesVisibility" <div onclick="toggleFavoritesView()" th:title="#{home.hideFavorites}" id="favouritesVisibility"
style="display: flex; align-items: center; cursor: pointer;"> style="display: flex; align-items: center; cursor: pointer;">
<span id="toggle-favourites-icon" class="material-symbols-rounded toggle-favourites" <span id="toggle-favourites-icon" class="material-symbols-rounded toggle-favourites"
style="font-size: 2rem; margin-left: 0.2rem;"> style="font-size: 2rem; margin-left: 0.2rem;">
visibility visibility
</span> </span>
</div> </div>
<a href="home" onclick="setAsDefault('home-legacy')" th:title="#{home.legacyHomepage}" <a href="home" onclick="setAsDefault('home-legacy')" th:title="#{home.legacyHomepage}"
style="text-decoration: none; color: inherit; cursor: pointer; display: flex; align-items: center;"> style="text-decoration: none; color: inherit; cursor: pointer; display: flex; align-items: center;">
<span class="material-symbols-rounded toggle-favourites" <span class="material-symbols-rounded toggle-favourites"
style="font-size: 2rem; margin-left: 0.2rem;"> style="font-size: 2rem; margin-left: 0.2rem;">
home home
</span> </span>
</a> </a>
<a th:if="${@shouldShow}" href="https://github.com/Stirling-Tools/Stirling-PDF/releases" <a th:if="${@shouldShow}" href="https://github.com/Stirling-Tools/Stirling-PDF/releases"
target="_blank" id="update-link" rel="noopener" th:title="#{settings.update}" target="_blank" id="update-link" rel="noopener" th:title="#{settings.update}"
style="text-decoration: none; color: inherit; cursor: pointer; display: flex; align-items: center;"> style="text-decoration: none; color: inherit; cursor: pointer; display: flex; align-items: center;">
<span class="material-symbols-rounded toggle-favourites" <span class="material-symbols-rounded toggle-favourites"
style="font-size: 2rem; margin-left: 0.2rem;"> style="font-size: 2rem; margin-left: 0.2rem;">
update update
</span> </span>
</a> </a>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div> </div>
</div> <div>
<div class="features-container" style=" border-top: 1px; </div>
<div class="features-container" style=" border-top: 1px;
border-top-style: solid; border-top-style: solid;
border-color: var(--md-nav-color-on-seperator); border-color: var(--md-nav-color-on-seperator);
margin-top: 1rem; margin-top: 1rem;
"> ">
<div class="feature-rows"> <div class="feature-rows">
<div id="groupFavorites" class="feature-group"> <div id="groupFavorites" class="feature-group">
<div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.favorite})}"> <div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.favorite})}">
</div> </div>
<div class="nav-group-container"> <div class="nav-group-container">
</div>
</div> </div>
<th:block th:insert="~{fragments/navElements.html :: navElements}"></th:block>
</div> </div>
<th:block th:insert="~{fragments/navElements.html :: navElements}"></th:block>
</div> </div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</div>
</div>
<!-- Survey Modal -->
<div class="modal fade" id="surveyModal" tabindex="-1" role="dialog" aria-labelledby="surveyModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="surveyModalLabel" th:text="#{survey.title}">Stirling-PDF Survey</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p th:text="#{survey.meeting.1}">If you're using Stirling PDF at work, we'd love to speak to you. We're offering free technical support in exchange for a 15 minute user discovery session.</p>
<p th:text="#{survey.meeting.2}">This is a chance to:</p>
<p><span>🛠️</span><span th:text="#{survey.meeting.3}">Get help with deployment, integrations, or troubleshooting</span></p>
<p><span>📢</span><span th:text="#{survey.meeting.4}">Provide direct feedback on performance, edge cases, and feature gaps</span></p>
<p><span>🔍</span><span th:text="#{survey.meeting.5}">Help us refine Stirling PDF for real-world enterprise use</span></p>
<p th:text="#{survey.meeting.6}">If you're interested, you can book time with our team directly.</p>
<p th:text="#{survey.meeting.7}">Looking forward to digging into your use cases and making Stirling PDF even better!</p>
<a href="https://calendly.com/d/cm4p-zz5-yy8/stirling-pdf-15-minute-group-discussion" target="_blank" class="btn btn-primary" id="takeSurvey2" th:text="#{survey.meeting.button}">Book meeting</a>
</br>
</br>
<p th:text="#{survey.meeting.notInterested}">Not a business and/or interested in a meeting?</p>
<p th:text="#{survey.please}">Please consider taking our survey!</p>
<a href="https://stirlingpdf.info/s/cm28y3niq000o56dv7liv8wsu" target="_blank" class="btn btn-primary"
id="takeSurvey" th:text="#{survey.button}">Take Survey</a>
</div>
<div class="modal-footer">
<div class="form-check mb-3">
<input type="checkbox" id="dontShowAgain">
<label for="dontShowAgain" th:text="#{survey.dontShowAgain}">Don't show again</label>
</div>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{close}">Close</button>
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- Survey Modal --> <!-- Analytics Modal -->
<div class="modal fade" id="surveyModal" tabindex="-1" role="dialog" aria-labelledby="surveyModalLabel" <div class="modal fade" id="analyticsModal" tabindex="-1" role="dialog" aria-labelledby="analyticsModalLabel"
aria-hidden="true"> aria-hidden="true" th:if="${@analyticsPrompt}">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document"> <div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="surveyModalLabel" th:text="#{survey.title}">Stirling-PDF Survey</h5> <h5 class="modal-title" id="analyticsModalLabel" th:text="#{analytics.title}">Do you want make Stirling PDF
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> better?</h5>
</div> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<div class="modal-body"> </div>
<p th:text="#{survey.meeting.1}">If you're using Stirling PDF at work, we'd love to speak to you. We're offering free technical support in exchange for a 15 minute user discovery session.</p> <div class="modal-body">
<p th:text="#{survey.meeting.2}">This is a chance to:</p> <p th:text="#{analytics.paragraph1}">Stirling PDF has opt in analytics to help us improve the product. We do
<p><span>🛠️</span><span th:text="#{survey.meeting.3}">Get help with deployment, integrations, or troubleshooting</span></p> not track any personal information or file contents.</p>
<p><span>📢</span><span th:text="#{survey.meeting.4}">Provide direct feedback on performance, edge cases, and feature gaps</span></p> <p th:text="#{analytics.paragraph2}">Please consider enabling analytics to help Stirling-PDF grow and to allow
<p><span>🔍</span><span th:text="#{survey.meeting.5}">Help us refine Stirling PDF for real-world enterprise use</span></p> us to understand our users better.</p>
<p th:text="#{survey.meeting.6}">If you're interested, you can book time with our team directly.</p> <p th:text="#{analytics.settings}">You can change the settings for analytics in the config/settings.yml file
<p th:text="#{survey.meeting.7}">Looking forward to digging into your use cases and making Stirling PDF even better!</p> </p>
<a href="https://calendly.com/d/cm4p-zz5-yy8/stirling-pdf-15-minute-group-discussion" target="_blank" class="btn btn-primary" id="takeSurvey2" th:text="#{survey.meeting.button}">Book meeting</a> </div>
</br> <div class="modal-footer justify-content-between">
</br> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" onclick="setAnalytics(false)"
<p th:text="#{survey.meeting.notInterested}">Not a business and/or interested in a meeting?</p> th:text="#{analytics.disable}">Disable analytics</button>
<button type="button" class="btn btn-primary" th:text="#{analytics.enable}"
<p th:text="#{survey.please}">Please consider taking our survey!</p> onclick="setAnalytics(true)">Enable analytics</button>
<a href="https://stirlingpdf.info/s/cm28y3niq000o56dv7liv8wsu" target="_blank" class="btn btn-primary"
id="takeSurvey" th:text="#{survey.button}">Take Survey</a>
</div>
<div class="modal-footer">
<div class="form-check mb-3">
<input type="checkbox" id="dontShowAgain">
<label for="dontShowAgain" th:text="#{survey.dontShowAgain}">Don't show again</label>
</div>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{close}">Close</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- Analytics Modal --> <style>
<div class="modal fade" id="analyticsModal" tabindex="-1" role="dialog" aria-labelledby="analyticsModalLabel" .favorite-icon {
aria-hidden="true" th:if="${@analyticsPrompt}"> cursor: pointer;
<div class="modal-dialog modal-dialog-centered" role="document"> width: 0rem;
<div class="modal-content"> font-size: 2rem;
<div class="modal-header"> }
<h5 class="modal-title" id="analyticsModalLabel" th:text="#{analytics.title}">Do you want make Stirling PDF
better?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p th:text="#{analytics.paragraph1}">Stirling PDF has opt in analytics to help us improve the product. We do
not track any personal information or file contents.</p>
<p th:text="#{analytics.paragraph2}">Please consider enabling analytics to help Stirling-PDF grow and to allow
us to understand our users better.</p>
<p th:text="#{analytics.settings}">You can change the settings for analytics in the config/settings.yml file
</p>
</div>
<div class="modal-footer justify-content-between">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" onclick="setAnalytics(false)"
th:text="#{analytics.disable}">Disable analytics</button>
<button type="button" class="btn btn-primary" th:text="#{analytics.enable}"
onclick="setAnalytics(true)">Enable analytics</button>
</div>
</div>
</div>
</div>
.toggle-favourites {
cursor: pointer;
}
<style> .toggle-favourites.active {
.favorite-icon { color: gold;
cursor: pointer; }
width: 0rem; </style>
font-size: 2rem; <script th:src="@{'/js/fetch-utils.js'}"></script>
} <script th:inline="javascript">
/*<![CDATA[*/
window.analyticsPromptBoolean = /*[[${@analyticsPrompt}]]*/ false;
/*]]>*/
.toggle-favourites { window.showSurvey = /*[[${showSurveyFromDocker}]]*/ true
cursor: pointer; </script>
} <script th:src="@{'/js/pages/home.js'}" th:inline="javascript"></script>
<script>
function applyScale() {
const baseWidth = 1440;
const baseHeight = 1000;
const scaleX = window.innerWidth / baseWidth;
const scaleY = window.innerHeight / baseHeight;
const scale = Math.max(0.9, Math.min(scaleX, scaleY)); // keep aspect ratio, honor minScale
const ui = document.getElementById('scale-wrap');
ui.style.transform = `scale(${scale*0.75})`;
}
.toggle-favourites.active { window.addEventListener('resize', applyScale);
color: gold; window.addEventListener('load', applyScale);
} </script>
</style>
<script th:src="@{'/js/fetch-utils.js'}"></script>
<script th:inline="javascript">
/*<![CDATA[*/
window.analyticsPromptBoolean = /*[[${@analyticsPrompt}]]*/ false;
/*]]>*/
window.showSurvey = /*[[${showSurveyFromDocker}]]*/ true
</script>
<script th:src="@{'/js/pages/home.js'}" th:inline="javascript"></script>
<script>
function applyScale() {
const baseWidth = 1440;
const baseHeight = 1000;
const scaleX = window.innerWidth / baseWidth;
const scaleY = window.innerHeight / baseHeight;
const scale = Math.max(0.9, Math.min(scaleX, scaleY)); // keep aspect ratio, honor minScale
const ui = document.getElementById('scale-wrap');
ui.style.transform = `scale(${scale*0.75})`;
}
window.addEventListener('resize', applyScale);
window.addEventListener('load', applyScale);
</script>
</body> </body>

View File

@ -8,13 +8,14 @@ import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.spdf.service.EndpointConfigurationService;
@Component @Component
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class EndpointInterceptor implements HandlerInterceptor { public class EndpointInterceptor implements HandlerInterceptor {
private final EndpointConfiguration endpointConfiguration; private final EndpointConfigurationService endpointConfigurationService;
@Override @Override
public boolean preHandle( public boolean preHandle(
@ -37,10 +38,10 @@ public class EndpointInterceptor implements HandlerInterceptor {
} }
log.debug("Request endpoint: {}", requestEndpoint); log.debug("Request endpoint: {}", requestEndpoint);
isEnabled = endpointConfiguration.isEndpointEnabled(requestEndpoint); isEnabled = endpointConfigurationService.isEndpointEnabled(requestEndpoint);
log.debug("Is endpoint enabled: {}", isEnabled); log.debug("Is endpoint enabled: {}", isEnabled);
} else { } else {
isEnabled = endpointConfiguration.isEndpointEnabled(requestURI); isEnabled = endpointConfigurationService.isEndpointEnabled(requestURI);
} }
if (!isEnabled) { if (!isEnabled) {

View File

@ -13,20 +13,21 @@ import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.common.configuration.RuntimePathConfig; import stirling.software.common.configuration.RuntimePathConfig;
import stirling.software.spdf.service.EndpointConfigurationService;
@Configuration @Configuration
@Slf4j @Slf4j
public class ExternalAppDepConfig { public class ExternalAppDepConfig {
private final EndpointConfiguration endpointConfiguration; private final EndpointConfigurationService endpointConfigurationService;
private final String weasyprintPath; private final String weasyprintPath;
private final String unoconvPath; private final String unoconvPath;
private final Map<String, List<String>> commandToGroupMapping; private final Map<String, List<String>> commandToGroupMapping;
public ExternalAppDepConfig( public ExternalAppDepConfig(
EndpointConfiguration endpointConfiguration, RuntimePathConfig runtimePathConfig) { EndpointConfigurationService endpointConfigurationService, RuntimePathConfig runtimePathConfig) {
this.endpointConfiguration = endpointConfiguration; this.endpointConfigurationService = endpointConfigurationService;
weasyprintPath = runtimePathConfig.getWeasyPrintPath(); weasyprintPath = runtimePathConfig.getWeasyPrintPath();
unoconvPath = runtimePathConfig.getUnoConvertPath(); unoconvPath = runtimePathConfig.getUnoConvertPath();
@ -62,7 +63,7 @@ public class ExternalAppDepConfig {
} }
private List<String> getAffectedFeatures(String group) { private List<String> getAffectedFeatures(String group) {
return endpointConfiguration.getEndpointsForGroup(group).stream() return endpointConfigurationService.getEndpointsForGroup(group).stream()
.map(endpoint -> formatEndpointAsFeature(endpoint)) .map(endpoint -> formatEndpointAsFeature(endpoint))
.toList(); .toList();
} }
@ -93,7 +94,7 @@ public class ExternalAppDepConfig {
if (affectedGroups != null) { if (affectedGroups != null) {
for (String group : affectedGroups) { for (String group : affectedGroups) {
List<String> affectedFeatures = getAffectedFeatures(group); List<String> affectedFeatures = getAffectedFeatures(group);
endpointConfiguration.disableGroup(group); endpointConfigurationService.disableGroup(group);
log.warn( log.warn(
"Missing dependency: {} - Disabling group: {} (Affected features: {})", "Missing dependency: {} - Disabling group: {} (Affected features: {})",
command, command,
@ -120,8 +121,8 @@ public class ExternalAppDepConfig {
if (!pythonAvailable) { if (!pythonAvailable) {
List<String> pythonFeatures = getAffectedFeatures("Python"); List<String> pythonFeatures = getAffectedFeatures("Python");
List<String> openCVFeatures = getAffectedFeatures("OpenCV"); List<String> openCVFeatures = getAffectedFeatures("OpenCV");
endpointConfiguration.disableGroup("Python"); endpointConfigurationService.disableGroup("Python");
endpointConfiguration.disableGroup("OpenCV"); endpointConfigurationService.disableGroup("OpenCV");
log.warn( log.warn(
"Missing dependency: Python - Disabling Python features: {} and OpenCV features: {}", "Missing dependency: Python - Disabling Python features: {} and OpenCV features: {}",
String.join(", ", pythonFeatures), String.join(", ", pythonFeatures),
@ -139,20 +140,20 @@ public class ExternalAppDepConfig {
int exitCode = process.waitFor(); int exitCode = process.waitFor();
if (exitCode != 0) { if (exitCode != 0) {
List<String> openCVFeatures = getAffectedFeatures("OpenCV"); List<String> openCVFeatures = getAffectedFeatures("OpenCV");
endpointConfiguration.disableGroup("OpenCV"); endpointConfigurationService.disableGroup("OpenCV");
log.warn( log.warn(
"OpenCV not available in Python - Disabling OpenCV features: {}", "OpenCV not available in Python - Disabling OpenCV features: {}",
String.join(", ", openCVFeatures)); String.join(", ", openCVFeatures));
} }
} catch (Exception e) { } catch (Exception e) {
List<String> openCVFeatures = getAffectedFeatures("OpenCV"); List<String> openCVFeatures = getAffectedFeatures("OpenCV");
endpointConfiguration.disableGroup("OpenCV"); endpointConfigurationService.disableGroup("OpenCV");
log.warn( log.warn(
"Error checking OpenCV: {} - Disabling OpenCV features: {}", "Error checking OpenCV: {} - Disabling OpenCV features: {}",
e.getMessage(), e.getMessage(),
String.join(", ", openCVFeatures)); String.join(", ", openCVFeatures));
} }
} }
endpointConfiguration.logDisabledEndpointsSummary(); endpointConfigurationService.logDisabledEndpointsSummary();
} }
} }

View File

@ -27,8 +27,14 @@ public class WebMvcConfig implements WebMvcConfigurer {
.addResourceLocations( .addResourceLocations(
"file:" + InstallationPathConfig.getStaticPath(), "file:" + InstallationPathConfig.getStaticPath(),
"classpath:/static/", "classpath:/static/",
"classpath:/stirling-pdf/" "classpath:/templates/",
// "/stirling-pdf/static/" "classpath:/stirling-pdf/",
"classpath:/stirling-pdf/static/",
"classpath:/stirling-pdf/templates/",
"/static/",
"/stirling-pdf/",
"/stirling-pdf/static/",
"/stirling-pdf/templates/"
); );
registry.addResourceHandler("/js/**").addResourceLocations("classpath:/static/js/"); registry.addResourceHandler("/js/**").addResourceLocations("classpath:/static/js/");
registry.addResourceHandler("/css/**").addResourceLocations("classpath:/static/css/"); registry.addResourceHandler("/css/**").addResourceLocations("classpath:/static/css/");

View File

@ -16,7 +16,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import stirling.software.spdf.config.EndpointConfiguration; import stirling.software.spdf.service.EndpointConfigurationService;
import stirling.software.common.configuration.InstallationPathConfig; import stirling.software.common.configuration.InstallationPathConfig;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.util.GeneralUtil; import stirling.software.common.util.GeneralUtil;
@ -29,7 +29,7 @@ import stirling.software.common.util.GeneralUtil;
public class SettingsController { public class SettingsController {
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
private final EndpointConfiguration endpointConfiguration; private final EndpointConfigurationService endpointConfigurationService;
@PostMapping("/update-enable-analytics") @PostMapping("/update-enable-analytics")
@Hidden @Hidden
@ -48,6 +48,6 @@ public class SettingsController {
@GetMapping("/get-endpoints-status") @GetMapping("/get-endpoints-status")
@Hidden @Hidden
public ResponseEntity<Map<String, Boolean>> getDisabledEndpoints() { public ResponseEntity<Map<String, Boolean>> getDisabledEndpoints() {
return ResponseEntity.ok(endpointConfiguration.getEndpointStatuses()); return ResponseEntity.ok(endpointConfigurationService.getEndpointStatuses());
} }
} }

View File

@ -23,7 +23,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import stirling.software.spdf.model.api.GeneralFile; import stirling.software.common.model.api.GeneralFile;
import stirling.software.common.configuration.RuntimePathConfig; import stirling.software.common.configuration.RuntimePathConfig;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.service.CustomPDFDocumentFactory;

View File

@ -23,7 +23,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import stirling.software.spdf.model.api.GeneralFile; import stirling.software.common.model.api.GeneralFile;
import stirling.software.common.configuration.RuntimePathConfig; import stirling.software.common.configuration.RuntimePathConfig;
import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.ProcessExecutor; import stirling.software.common.util.ProcessExecutor;

View File

@ -1,4 +1,4 @@
package stirling.software.SPDF.controller.api.misc; package stirling.software.spdf.controller.api.misc;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -49,13 +49,13 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.EndpointConfiguration;
import stirling.software.SPDF.model.api.misc.OptimizePdfRequest;
import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.GeneralUtil; import stirling.software.common.util.GeneralUtil;
import stirling.software.common.util.ProcessExecutor; import stirling.software.common.util.ProcessExecutor;
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult; import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
import stirling.software.common.util.WebResponseUtils; import stirling.software.common.util.WebResponseUtils;
import stirling.software.spdf.service.EndpointConfigurationService;
import stirling.software.spdf.model.api.misc.OptimizePdfRequest;
@RestController @RestController
@RequestMapping("/api/v1/misc") @RequestMapping("/api/v1/misc")
@ -68,9 +68,9 @@ public class CompressController {
public CompressController( public CompressController(
CustomPDFDocumentFactory pdfDocumentFactory, CustomPDFDocumentFactory pdfDocumentFactory,
EndpointConfiguration endpointConfiguration) { EndpointConfigurationService endpointConfigurationService) {
this.pdfDocumentFactory = pdfDocumentFactory; this.pdfDocumentFactory = pdfDocumentFactory;
this.qpdfEnabled = endpointConfiguration.isGroupEnabled("qpdf"); this.qpdfEnabled = endpointConfigurationService.isGroupEnabled("qpdf");
} }
@Data @Data

View File

@ -1,4 +1,4 @@
package stirling.software.spdf.config; package stirling.software.spdf.service;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -13,9 +13,9 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
@Service
@Slf4j @Slf4j
public class EndpointConfiguration { @Service
public class EndpointConfigurationService {
private static final String REMOVE_BLANKS = "remove-blanks"; private static final String REMOVE_BLANKS = "remove-blanks";
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
@ -23,7 +23,7 @@ public class EndpointConfiguration {
private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>(); private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>();
private final boolean runningProOrHigher; private final boolean runningProOrHigher;
public EndpointConfiguration( public EndpointConfigurationService(
ApplicationProperties applicationProperties, ApplicationProperties applicationProperties,
@Qualifier("runningProOrHigher") boolean runningProOrHigher) { @Qualifier("runningProOrHigher") boolean runningProOrHigher) {
this.applicationProperties = applicationProperties; this.applicationProperties = applicationProperties;

View File

@ -40,7 +40,7 @@
</root> </root>
<!-- Specific Logger --> <!-- Specific Logger -->
<logger name="stirling.software.spdf.config.security.CustomAuthenticationFailureHandler" <logger name="stirling.software.SPDF.config.security.CustomAuthenticationFailureHandler"
level="ERROR" additivity="false"> level="ERROR" additivity="false">
<appender-ref ref="CONSOLE"/> <appender-ref ref="CONSOLE"/>
<appender-ref ref="AUTHLOG"/> <appender-ref ref="AUTHLOG"/>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -517,13 +517,13 @@ home.showJS.title=Afficher le JavaScript
home.showJS.desc=Recherche et affiche tout JavaScript injecté dans un PDF. home.showJS.desc=Recherche et affiche tout JavaScript injecté dans un PDF.
showJS.tags=JS showJS.tags=JS
home.autoRedact.title=Censure automatique home.autoRedact.title=Caviardage automatique
home.autoRedact.desc=Censurer automatiquement les informations sensibles d'un PDF. home.autoRedact.desc=Caviardez automatiquement les informations sensibles d'un PDF.
autoRedact.tags=caviarder,rédiger,censurer,redact,auto autoRedact.tags=caviarder,redact,auto,Masquer,noircir,noir,marqueur,caché,rédiger,censurer
home.redact.title=Censure manuelle home.redact.title=Caviardage manuel
home.redact.desc=Censurer un PDF en fonction de texte sélectionné, formes dessinées et/ou des pages sélectionnées. home.redact.desc=Caviarder un PDF en fonction de texte sélectionné, formes dessinées et/ou des pages sélectionnées.
redact.tags=Redact,Hide,black out,black,marker,hidden,manual,caviarder,rédiger,censurer redact.tags=Caviarder,Redact,Masquer,noircir,noir,marqueur,caché,rédiger,censurer
home.tableExtraxt.title=PDF en CSV home.tableExtraxt.title=PDF en CSV
home.tableExtraxt.desc=Extrait les tableaux d'un PDF et les transforme en CSV. home.tableExtraxt.desc=Extrait les tableaux d'un PDF et les transforme en CSV.
@ -624,31 +624,31 @@ autoRedact.convertPDFToImageLabel=Convertir un PDF en PDF-Image (utilisé pour s
autoRedact.submitButton=Caviarder autoRedact.submitButton=Caviarder
#redact #redact
redact.title=Rédaction manuelle redact.title=Caviardage manuel
redact.header=Rédaction manuelle redact.header=Caviardage manuel
redact.submit=Rédiger redact.submit=Caviarder
redact.textBasedRedaction=Rédaction en fonction de texte redact.textBasedRedaction=Caviarder du texte
redact.pageBasedRedaction=Rédaction en fonction de pages redact.pageBasedRedaction=Caviarder des pages
redact.convertPDFToImageLabel=Convertir en PDF-Image (pour supprimer le texte derrière le rectangle) redact.convertPDFToImageLabel=Convertir en PDF-Image (pour supprimer le texte derrière le rectangle)
redact.pageRedactionNumbers.title=Pages redact.pageRedactionNumbers.title=Pages
redact.pageRedactionNumbers.placeholder=(ex: 1,2,8 ou 4,7,12-16 ou 2n-1) redact.pageRedactionNumbers.placeholder=(ex: 1,2,8 ou 4,7,12-16 ou 2n-1)
redact.redactionColor.title=Couleur redact.redactionColor.title=Couleur
redact.export=Exporter redact.export=Exporter
redact.upload=Téléverser redact.upload=Téléverser
redact.boxRedaction=Dessiner le rectangle à rédiger redact.boxRedaction=Tracer le rectangle à caviarder
redact.zoom=Zoom redact.zoom=Zoom
redact.zoomIn=Zoom avant redact.zoomIn=Zoom avant
redact.zoomOut=Zoom arrière redact.zoomOut=Zoom arrière
redact.nextPage=Page suivante redact.nextPage=Page suivante
redact.previousPage=Page précédente redact.previousPage=Page précédente
redact.toggleSidebar=Toggle Sidebar redact.toggleSidebar=Montrer la barre latérale
redact.showThumbnails=Afficher les miniatures redact.showThumbnails=Afficher les miniatures
redact.showDocumentOutline=Montrer les contours du document (double-click pour agrandir/réduire tous les éléments) redact.showDocumentOutline=Montrer les contours du document (double-click pour agrandir/réduire tous les éléments)
redact.showAttatchments=Montrer les éléments attachés redact.showAttatchments=Montrer les éléments attachés
redact.showLayers=Montrer les calques (double-click pour réinitialiser tous les calques à l'état par défaut) redact.showLayers=Montrer les calques (double-click pour réinitialiser tous les calques à l'état par défaut)
redact.colourPicker=Sélection de couleur redact.colourPicker=Sélection de couleur
redact.findCurrentOutlineItem=Trouver l'élément de contour courrant redact.findCurrentOutlineItem=Trouver l'élément de contour courrant
redact.applyChanges=Apply Changes redact.applyChanges=Appliquer les changements
#showJS #showJS
showJS.title=Afficher le JavaScript showJS.title=Afficher le JavaScript

View File

@ -609,7 +609,7 @@ login.userIsDisabled=L'utente è disattivato, l'accesso è attualmente bloccato
login.alreadyLoggedIn=Hai già effettuato l'accesso a login.alreadyLoggedIn=Hai già effettuato l'accesso a
login.alreadyLoggedIn2=dispositivi. Esci dai dispositivi e riprova. login.alreadyLoggedIn2=dispositivi. Esci dai dispositivi e riprova.
login.toManySessions=Hai troppe sessioni attive login.toManySessions=Hai troppe sessioni attive
login.logoutMessage=You have been logged out. login.logoutMessage=Sei stato disconnesso.
#auto-redact #auto-redact
autoRedact.title=Redazione automatica autoRedact.title=Redazione automatica

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -77,6 +77,14 @@ premium:
apiKey: '' apiKey: ''
appId: '' appId: ''
mail:
enabled: false # set to 'true' to enable sending emails
host: smtp.example.com # SMTP server hostname
port: 587 # SMTP server port
username: '' # SMTP server username
password: '' # SMTP server password
from: '' # sender email address
legal: legal:
termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder

View File

@ -270,7 +270,7 @@
{ {
"moduleName": "com.opencsv:opencsv", "moduleName": "com.opencsv:opencsv",
"moduleUrl": "http://opencsv.sf.net", "moduleUrl": "http://opencsv.sf.net",
"moduleVersion": "5.10", "moduleVersion": "5.11",
"moduleLicense": "Apache 2", "moduleLicense": "Apache 2",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
@ -546,7 +546,7 @@
{ {
"moduleName": "io.micrometer:micrometer-commons", "moduleName": "io.micrometer:micrometer-commons",
"moduleUrl": "https://github.com/micrometer-metrics/micrometer", "moduleUrl": "https://github.com/micrometer-metrics/micrometer",
"moduleVersion": "1.14.5", "moduleVersion": "1.14.6",
"moduleLicense": "The Apache Software License, Version 2.0", "moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
@ -560,21 +560,21 @@
{ {
"moduleName": "io.micrometer:micrometer-jakarta9", "moduleName": "io.micrometer:micrometer-jakarta9",
"moduleUrl": "https://github.com/micrometer-metrics/micrometer", "moduleUrl": "https://github.com/micrometer-metrics/micrometer",
"moduleVersion": "1.14.5", "moduleVersion": "1.14.6",
"moduleLicense": "The Apache Software License, Version 2.0", "moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{ {
"moduleName": "io.micrometer:micrometer-observation", "moduleName": "io.micrometer:micrometer-observation",
"moduleUrl": "https://github.com/micrometer-metrics/micrometer", "moduleUrl": "https://github.com/micrometer-metrics/micrometer",
"moduleVersion": "1.14.5", "moduleVersion": "1.14.6",
"moduleLicense": "The Apache Software License, Version 2.0", "moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{ {
"moduleName": "io.micrometer:micrometer-registry-prometheus", "moduleName": "io.micrometer:micrometer-registry-prometheus",
"moduleUrl": "https://github.com/micrometer-metrics/micrometer", "moduleUrl": "https://github.com/micrometer-metrics/micrometer",
"moduleVersion": "1.14.5", "moduleVersion": "1.14.6",
"moduleLicense": "The Apache Software License, Version 2.0", "moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
@ -623,21 +623,21 @@
{ {
"moduleName": "io.swagger.core.v3:swagger-annotations-jakarta", "moduleName": "io.swagger.core.v3:swagger-annotations-jakarta",
"moduleUrl": "https://github.com/swagger-api/swagger-core/modules/swagger-annotations", "moduleUrl": "https://github.com/swagger-api/swagger-core/modules/swagger-annotations",
"moduleVersion": "2.2.29", "moduleVersion": "2.2.30",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "io.swagger.core.v3:swagger-core-jakarta", "moduleName": "io.swagger.core.v3:swagger-core-jakarta",
"moduleUrl": "https://github.com/swagger-api/swagger-core/modules/swagger-core", "moduleUrl": "https://github.com/swagger-api/swagger-core/modules/swagger-core",
"moduleVersion": "2.2.29", "moduleVersion": "2.2.30",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "io.swagger.core.v3:swagger-models-jakarta", "moduleName": "io.swagger.core.v3:swagger-models-jakarta",
"moduleUrl": "https://github.com/swagger-api/swagger-core/modules/swagger-models", "moduleUrl": "https://github.com/swagger-api/swagger-core/modules/swagger-models",
"moduleVersion": "2.2.29", "moduleVersion": "2.2.30",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
@ -871,7 +871,7 @@
{ {
"moduleName": "org.apache.pdfbox:fontbox", "moduleName": "org.apache.pdfbox:fontbox",
"moduleUrl": "https://pdfbox.apache.org", "moduleUrl": "https://pdfbox.apache.org",
"moduleVersion": "3.0.4", "moduleVersion": "3.0.5",
"moduleLicense": "Apache-2.0", "moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
@ -884,28 +884,28 @@
{ {
"moduleName": "org.apache.pdfbox:pdfbox", "moduleName": "org.apache.pdfbox:pdfbox",
"moduleUrl": "https://pdfbox.apache.org", "moduleUrl": "https://pdfbox.apache.org",
"moduleVersion": "3.0.4", "moduleVersion": "3.0.5",
"moduleLicense": "Apache-2.0", "moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{ {
"moduleName": "org.apache.pdfbox:pdfbox-io", "moduleName": "org.apache.pdfbox:pdfbox-io",
"moduleUrl": "https://pdfbox.apache.org", "moduleUrl": "https://pdfbox.apache.org",
"moduleVersion": "3.0.4", "moduleVersion": "3.0.5",
"moduleLicense": "Apache-2.0", "moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{ {
"moduleName": "org.apache.pdfbox:preflight", "moduleName": "org.apache.pdfbox:preflight",
"moduleUrl": "https://pdfbox.apache.org", "moduleUrl": "https://pdfbox.apache.org",
"moduleVersion": "3.0.4", "moduleVersion": "3.0.5",
"moduleLicense": "Apache-2.0", "moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{ {
"moduleName": "org.apache.pdfbox:xmpbox", "moduleName": "org.apache.pdfbox:xmpbox",
"moduleUrl": "https://pdfbox.apache.org", "moduleUrl": "https://pdfbox.apache.org",
"moduleVersion": "3.0.4", "moduleVersion": "3.0.5",
"moduleLicense": "Apache-2.0", "moduleLicense": "Apache-2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
@ -919,7 +919,7 @@
{ {
"moduleName": "org.apache.tomcat.embed:tomcat-embed-el", "moduleName": "org.apache.tomcat.embed:tomcat-embed-el",
"moduleUrl": "https://tomcat.apache.org/", "moduleUrl": "https://tomcat.apache.org/",
"moduleVersion": "10.1.39", "moduleVersion": "10.1.40",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
@ -946,7 +946,7 @@
{ {
"moduleName": "org.aspectj:aspectjweaver", "moduleName": "org.aspectj:aspectjweaver",
"moduleUrl": "https://www.eclipse.org/aspectj/", "moduleUrl": "https://www.eclipse.org/aspectj/",
"moduleVersion": "1.9.23", "moduleVersion": "1.9.24",
"moduleLicense": "Eclipse Public License - v 2.0", "moduleLicense": "Eclipse Public License - v 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt"
}, },
@ -1011,185 +1011,192 @@
"moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception",
"moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
}, },
{
"moduleName": "org.eclipse.angus:jakarta.mail",
"moduleUrl": "https://www.eclipse.org",
"moduleVersion": "2.0.3",
"moduleLicense": "GPL2 w/ CPE",
"moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
},
{ {
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-client", "moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-client",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-common", "moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-common",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-server", "moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-server",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jetty-server", "moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jetty-server",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-servlet", "moduleName": "org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-servlet",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-annotations", "moduleName": "org.eclipse.jetty.ee10:jetty-ee10-annotations",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-plus", "moduleName": "org.eclipse.jetty.ee10:jetty-ee10-plus",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-servlet", "moduleName": "org.eclipse.jetty.ee10:jetty-ee10-servlet",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-servlets", "moduleName": "org.eclipse.jetty.ee10:jetty-ee10-servlets",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.ee10:jetty-ee10-webapp", "moduleName": "org.eclipse.jetty.ee10:jetty-ee10-webapp",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-client", "moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-client",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-common", "moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-common",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-server", "moduleName": "org.eclipse.jetty.websocket:jetty-websocket-core-server",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-jetty-api", "moduleName": "org.eclipse.jetty.websocket:jetty-websocket-jetty-api",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty.websocket:jetty-websocket-jetty-common", "moduleName": "org.eclipse.jetty.websocket:jetty-websocket-jetty-common",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty:jetty-alpn-client", "moduleName": "org.eclipse.jetty:jetty-alpn-client",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty:jetty-client", "moduleName": "org.eclipse.jetty:jetty-client",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty:jetty-ee", "moduleName": "org.eclipse.jetty:jetty-ee",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty:jetty-http", "moduleName": "org.eclipse.jetty:jetty-http",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty:jetty-io", "moduleName": "org.eclipse.jetty:jetty-io",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty:jetty-plus", "moduleName": "org.eclipse.jetty:jetty-plus",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty:jetty-security", "moduleName": "org.eclipse.jetty:jetty-security",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty:jetty-server", "moduleName": "org.eclipse.jetty:jetty-server",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty:jetty-session", "moduleName": "org.eclipse.jetty:jetty-session",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty:jetty-util", "moduleName": "org.eclipse.jetty:jetty-util",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
{ {
"moduleName": "org.eclipse.jetty:jetty-xml", "moduleName": "org.eclipse.jetty:jetty-xml",
"moduleUrl": "https://jetty.org/", "moduleUrl": "https://jetty.org/",
"moduleVersion": "12.0.18", "moduleVersion": "12.0.19",
"moduleLicense": "Eclipse Public License - Version 2.0", "moduleLicense": "Eclipse Public License - Version 2.0",
"moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/" "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-2.0/"
}, },
@ -1231,10 +1238,16 @@
{ {
"moduleName": "org.hibernate.orm:hibernate-core", "moduleName": "org.hibernate.orm:hibernate-core",
"moduleUrl": "https://www.hibernate.org/orm/6.6", "moduleUrl": "https://www.hibernate.org/orm/6.6",
"moduleVersion": "6.6.11.Final", "moduleVersion": "6.6.13.Final",
"moduleLicense": "GNU Library General Public License v2.1 or later", "moduleLicense": "GNU Library General Public License v2.1 or later",
"moduleLicenseUrl": "https://www.opensource.org/licenses/LGPL-2.1" "moduleLicenseUrl": "https://www.opensource.org/licenses/LGPL-2.1"
}, },
{
"moduleName": "org.hibernate.validator:hibernate-validator",
"moduleVersion": "8.0.2.Final",
"moduleLicense": "Apache License 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{ {
"moduleName": "org.jboss.logging:jboss-logging", "moduleName": "org.jboss.logging:jboss-logging",
"moduleUrl": "http://www.jboss.org", "moduleUrl": "http://www.jboss.org",
@ -1423,187 +1436,201 @@
}, },
{ {
"moduleName": "org.springdoc:springdoc-openapi-starter-common", "moduleName": "org.springdoc:springdoc-openapi-starter-common",
"moduleVersion": "2.8.6", "moduleVersion": "2.8.8",
"moduleLicense": "The Apache License, Version 2.0", "moduleLicense": "The Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{ {
"moduleName": "org.springdoc:springdoc-openapi-starter-webmvc-api", "moduleName": "org.springdoc:springdoc-openapi-starter-webmvc-api",
"moduleVersion": "2.8.6", "moduleVersion": "2.8.8",
"moduleLicense": "The Apache License, Version 2.0", "moduleLicense": "The Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{ {
"moduleName": "org.springdoc:springdoc-openapi-starter-webmvc-ui", "moduleName": "org.springdoc:springdoc-openapi-starter-webmvc-ui",
"moduleVersion": "2.8.6", "moduleVersion": "2.8.8",
"moduleLicense": "The Apache License, Version 2.0", "moduleLicense": "The Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot", "moduleName": "org.springframework.boot:spring-boot",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-actuator", "moduleName": "org.springframework.boot:spring-boot-actuator",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-actuator-autoconfigure", "moduleName": "org.springframework.boot:spring-boot-actuator-autoconfigure",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-autoconfigure", "moduleName": "org.springframework.boot:spring-boot-autoconfigure",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-devtools", "moduleName": "org.springframework.boot:spring-boot-devtools",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-starter", "moduleName": "org.springframework.boot:spring-boot-starter",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-starter-actuator", "moduleName": "org.springframework.boot:spring-boot-starter-actuator",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-starter-data-jpa", "moduleName": "org.springframework.boot:spring-boot-starter-data-jpa",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-starter-jdbc", "moduleName": "org.springframework.boot:spring-boot-starter-jdbc",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-starter-jetty", "moduleName": "org.springframework.boot:spring-boot-starter-jetty",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-starter-json", "moduleName": "org.springframework.boot:spring-boot-starter-json",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-starter-logging", "moduleName": "org.springframework.boot:spring-boot-starter-logging",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-mail",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-starter-oauth2-client", "moduleName": "org.springframework.boot:spring-boot-starter-oauth2-client",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-starter-security", "moduleName": "org.springframework.boot:spring-boot-starter-security",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-starter-thymeleaf", "moduleName": "org.springframework.boot:spring-boot-starter-thymeleaf",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework.boot:spring-boot-starter-validation",
"moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.boot:spring-boot-starter-web", "moduleName": "org.springframework.boot:spring-boot-starter-web",
"moduleUrl": "https://spring.io/projects/spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.data:spring-data-commons", "moduleName": "org.springframework.data:spring-data-commons",
"moduleUrl": "https://spring.io/projects/spring-data", "moduleUrl": "https://spring.io/projects/spring-data",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.data:spring-data-jpa", "moduleName": "org.springframework.data:spring-data-jpa",
"moduleUrl": "https://projects.spring.io/spring-data-jpa", "moduleUrl": "https://projects.spring.io/spring-data-jpa",
"moduleVersion": "3.4.4", "moduleVersion": "3.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.security:spring-security-config", "moduleName": "org.springframework.security:spring-security-config",
"moduleUrl": "https://spring.io/projects/spring-security", "moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.4.4", "moduleVersion": "6.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.security:spring-security-core", "moduleName": "org.springframework.security:spring-security-core",
"moduleUrl": "https://spring.io/projects/spring-security", "moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.4.4", "moduleVersion": "6.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.security:spring-security-crypto", "moduleName": "org.springframework.security:spring-security-crypto",
"moduleUrl": "https://spring.io/projects/spring-security", "moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.4.4", "moduleVersion": "6.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.security:spring-security-oauth2-client", "moduleName": "org.springframework.security:spring-security-oauth2-client",
"moduleUrl": "https://spring.io/projects/spring-security", "moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.4.4", "moduleVersion": "6.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.security:spring-security-oauth2-core", "moduleName": "org.springframework.security:spring-security-oauth2-core",
"moduleUrl": "https://spring.io/projects/spring-security", "moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.4.4", "moduleVersion": "6.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework.security:spring-security-oauth2-jose", "moduleName": "org.springframework.security:spring-security-oauth2-jose",
"moduleUrl": "https://spring.io/projects/spring-security", "moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.4.4", "moduleVersion": "6.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
@ -1617,7 +1644,7 @@
{ {
"moduleName": "org.springframework.security:spring-security-web", "moduleName": "org.springframework.security:spring-security-web",
"moduleUrl": "https://spring.io/projects/spring-security", "moduleUrl": "https://spring.io/projects/spring-security",
"moduleVersion": "6.4.4", "moduleVersion": "6.4.5",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
@ -1631,49 +1658,56 @@
{ {
"moduleName": "org.springframework:spring-aop", "moduleName": "org.springframework:spring-aop",
"moduleUrl": "https://github.com/spring-projects/spring-framework", "moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.5", "moduleVersion": "6.2.6",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework:spring-aspects", "moduleName": "org.springframework:spring-aspects",
"moduleUrl": "https://github.com/spring-projects/spring-framework", "moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.5", "moduleVersion": "6.2.6",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework:spring-beans", "moduleName": "org.springframework:spring-beans",
"moduleUrl": "https://github.com/spring-projects/spring-framework", "moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.5", "moduleVersion": "6.2.6",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework:spring-context", "moduleName": "org.springframework:spring-context",
"moduleUrl": "https://github.com/spring-projects/spring-framework", "moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.5", "moduleVersion": "6.2.6",
"moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
},
{
"moduleName": "org.springframework:spring-context-support",
"moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.6",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework:spring-core", "moduleName": "org.springframework:spring-core",
"moduleUrl": "https://github.com/spring-projects/spring-framework", "moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.5", "moduleVersion": "6.2.6",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework:spring-expression", "moduleName": "org.springframework:spring-expression",
"moduleUrl": "https://github.com/spring-projects/spring-framework", "moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.5", "moduleVersion": "6.2.6",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework:spring-jcl", "moduleName": "org.springframework:spring-jcl",
"moduleUrl": "https://github.com/spring-projects/spring-framework", "moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.5", "moduleVersion": "6.2.6",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
@ -1687,21 +1721,21 @@
{ {
"moduleName": "org.springframework:spring-orm", "moduleName": "org.springframework:spring-orm",
"moduleUrl": "https://github.com/spring-projects/spring-framework", "moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.5", "moduleVersion": "6.2.6",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework:spring-tx", "moduleName": "org.springframework:spring-tx",
"moduleUrl": "https://github.com/spring-projects/spring-framework", "moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.5", "moduleVersion": "6.2.6",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
{ {
"moduleName": "org.springframework:spring-web", "moduleName": "org.springframework:spring-web",
"moduleUrl": "https://github.com/spring-projects/spring-framework", "moduleUrl": "https://github.com/spring-projects/spring-framework",
"moduleVersion": "6.2.5", "moduleVersion": "6.2.6",
"moduleLicense": "Apache License, Version 2.0", "moduleLicense": "Apache License, Version 2.0",
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
}, },
@ -1746,7 +1780,7 @@
{ {
"moduleName": "org.webjars:swagger-ui", "moduleName": "org.webjars:swagger-ui",
"moduleUrl": "https://www.webjars.org", "moduleUrl": "https://www.webjars.org",
"moduleVersion": "5.20.1", "moduleVersion": "5.21.0",
"moduleLicense": "Apache-2.0" "moduleLicense": "Apache-2.0"
}, },
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,4 @@
.buttons-container {
margin-top: 20px;
text-align: center;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,60 @@
#footer {
display: flex;
flex-direction: column; /* Stack children vertically */
justify-content: center;
align-items: center;
width: 100%;
}
.footer-center {
display: flex;
align-items: center; /* Center children horizontally */
flex-grow: 1;
flex-direction: column;
}
.footer-powered-by {
margin-top: auto; /* Pushes the text to the bottom */
text-align: center; /* Centers the text inside the div */
width: 100%; /* Full width to center the text properly */
}
.stirling-link {
text-decoration: none; /* Remove the underline */
color: inherit; /* Keep the text color the same as the surrounding text */
cursor: pointer; /* Change the cursor to indicate it's clickable */
font-weight: bold; /* Make it bold to subtly hint that it's clickable */
transition: color 0.3s ease; /* Add a smooth transition effect for color change on hover */
}
.stirling-link:hover {
color: #007BFF; /* Change the color on hover to a noticeable link color */
}
.footer-icon {
font-size: 2rem;
}
.footer-link {
text-decoration: none;
cursor: pointer;
}
.footer-link-list {
display: flex;
flex-direction: row; /* Align links in a row */
}
/* Responsive styles for smaller screens */
@media (max-width: 650px) {
.footer-link {
font-size: 0.8rem; /* Adjust font size for smaller screens */
}
}
@media (max-width: 550px) {
.footer-link {
font-size: 1rem; /* Adjust font size for smaller screens */
}
.footer-link-list{
flex-direction: column; /* Stack links vertically on smaller screens */
}
}

View File

@ -1,7 +1,8 @@
#page-container { #page-container {
min-height: 100vh; height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow-x: clip;
} }
#content-wrap { #content-wrap {

View File

@ -0,0 +1,9 @@
td a {
text-decoration: none;
}
td a:hover,
td a:focus {
text-decoration: underline;
/* Adds underline on hover/focus for clarity */
}

View File

@ -162,6 +162,7 @@ html[dir="rtl"] .lang-dropdown-item-wrapper {
text-wrap: wrap; text-wrap: wrap;
word-break: break-word; word-break: break-word;
width: 80%; width: 80%;
font-size: large;
} }
.close-icon { .close-icon {
@ -476,6 +477,9 @@ html[dir="rtl"] .dropdown-menu[data-bs-popper] {
display: flex; display: flex;
gap: 30px; gap: 30px;
justify-content: center; justify-content: center;
width: 140%;
position: relative;
left: -20%;
} }
.feature-group { .feature-group {

View File

@ -0,0 +1,970 @@
/* Dark Mode Styles */
body,
select,
textarea {
background-color: var(--md-sys-color-surface);
color: var(--md-sys-color-on-surface);
}
.transition-theme {
transition: background 0.5s ease, color 0.5s ease, border 0.5s ease;
}
/*.global-buttons-container input:disabled::-webkit-input-placeholder { !* WebKit browsers *!*/
/* color: #98A0AB;*/
/*}*/
/*.global-buttons-container input:disabled:-moz-placeholder { !* Mozilla Firefox 4 to 18 *!*/
/* color: #98A0AB;*/
/*}*/
/*.global-buttons-container input:disabled::-moz-placeholder { !* Mozilla Firefox 19+ *!*/
/* color: #98A0AB;*/
/*}*/
/*.global-buttons-container input:disabled:-ms-input-placeholder { !* Internet Explorer 10+ *!*/
/* color: #98A0AB;*/
/*}*/
/* Scrollbar */
*::-webkit-scrollbar {
background: var(--md-sys-color-surface);
width: 1rem;
}
*::-webkit-scrollbar-track {
background: var(--md-sys-color-surface);
}
*::-webkit-scrollbar-thumb {
border-radius: 2rem;
background-color: var(--md-sys-color-surface-5);
border: 5px solid var(--md-sys-color-surface-5);
}
*::-webkit-scrollbar-corner {
background-color: var(--md-sys-color-surface);
}
/* Alerts */
.alert {
border-radius: 3rem;
}
/* Table */
td {
word-break: break-word;
}
.input-group-append {
margin: 0rem 0.5rem !important;
}
.card-header {
background-color: transparent;
border-bottom: none;
}
.bg-card {
background-color: var(--md-sys-color-surface-5);
border-radius: 3rem;
padding: 2.5rem;
}
.card {
padding: 1.25rem;
border-radius: 2rem;
background-color: var(--md-sys-color-surface-5);
border: none;
}
/* Modal */
.modal-content {
background-color: var(--md-sys-color-surface);
border-radius: 2rem;
border: transparent;
}
.modal-header,
.modal-body,
.modal-footer {
background-color: var(--md-sys-color-surface-5);
border: none;
}
.modal-header {
border-radius: 2rem 2rem 0rem 0rem;
padding: 1.5rem 2rem 0.5rem;
}
.modal-body{
padding: 0.5rem 2rem;
}
.modal-footer {
border-radius: 0rem 0rem 2rem 2rem;
padding: 0.5rem 2rem 1.5rem;
}
/* Icon fill */
.material-symbols-rounded {
vertical-align: text-top;
}
/* Navbar Icon*/
.nav-icon {
color: var(--md-sys-color-surface);
}
.sign .nav-icon,
.sign.tool-header-icon {
color: var(--md-nav-on-section-color-sign);
background-color: var(--md-nav-section-color-sign);
}
.organize .nav-icon,
.organize.tool-header-icon {
color: var(--md-nav-on-section-color-organize);
background-color: var(--md-nav-section-color-organize);
}
.convert .nav-icon,
.convert.tool-header-icon {
color: var(--md-nav-on-section-color-convert);
background-color: var(--md-nav-section-color-convert);
}
.convertto .nav-icon,
.convertto.tool-header-icon {
color: var(--md-nav-on-section-color-convertto);
background-color: var(--md-nav-section-color-convertto);
}
.security .nav-icon,
.security.tool-header-icon {
color: var(--md-nav-on-section-color-security);
background-color: var(--md-nav-section-color-security);
}
.other .nav-icon,
.other.tool-header-icon {
color: var(--md-nav-on-section-color-other);
background-color: var(--md-nav-section-color-other);
}
.advance .nav-icon,
.advance.tool-header-icon {
color: var(--md-nav-on-section-color-advance);
background-color: var(--md-nav-section-color-advance);
}
.image .nav-icon,
.image.tool-header-icon {
color: var(--md-nav-on-section-color-image);
background-color: var(--md-nav-section-color-image);
}
.word .nav-icon,
.word.tool-header-icon {
color: var(--md-nav-on-section-color-word);
background-color: var(--md-nav-section-color-word);
}
.ppt .nav-icon,
.ppt.tool-header-icon {
color: var(--md-nav-on-section-color-ppt);
background-color: var(--md-nav-section-color-ppt);
}
/* Tool Page Header*/
.tool-header {
margin-bottom: 2rem;
}
.tool-header .tool-header-icon {
margin: 0px 1rem;
height: 4rem;
width: 4rem;
border-radius: 25px;
font-size: 3rem;
padding: 0.5rem;
vertical-align: middle;
pointer-events: none;
user-select: none;
-webkit-user-select: none;
-webkit-tap-highlight-color: rgb(0 0 0 / 0%);
}
.tool-header .tool-header-text {
font-size: 2.5rem;
font-weight: 400;
vertical-align: middle;
}
/* Home Card Colors*/
.feature-card .nav-icon {
vertical-align: middle;
font-size: 2rem !important;
padding: 0.75rem;
border-radius: 0.9rem;
color: var(--md-sys-color-surface);
}
.feature-card .sign .nav-icon {
color: var(--md-nav-on-section-color-sign);
background-color: var(--md-nav-section-color-sign);
}
.feature-card .organize .nav-icon {
color: var(--md-nav-on-section-color-organize);
background-color: var(--md-nav-section-color-organize);
}
.feature-card .convert .nav-icon {
color: var(--md-nav-on-section-color-convert);
background-color: var(--md-nav-section-color-convert);
}
.feature-card .convertto .nav-icon {
color: var(--md-nav-on-section-color-convertto);
background-color: var(--md-nav-section-color-convertto);
}
.feature-card .security .nav-icon {
color: var(--md-nav-on-section-color-security);
background-color: var(--md-nav-section-color-security);
}
.feature-card .other .nav-icon {
color: var(--md-nav-on-section-color-other);
background-color: var(--md-nav-section-color-other);
}
.feature-card .advance .nav-icon {
color: var(--md-nav-on-section-color-advance);
background-color: var(--md-nav-section-color-advance);
}
.feature-card .image .nav-icon {
color: var(--md-nav-on-section-color-image);
background-color: var(--md-nav-section-color-image);
}
.feature-card .word .nav-icon {
color: var(--md-nav-on-section-color-word);
background-color: var(--md-nav-section-color-word);
}
.feature-card .ppt .nav-icon {
color: var(--md-nav-on-section-color-ppt);
background-color: var(--md-nav-section-color-ppt);
}
/* Buttons Components */
.btn {
border-radius: 1.25rem;
}
.btn-close {
width: auto;
height: auto;
color: var(--md-sys-color-on-surface);
background: transparent;
}
.btn-close:hover {
color: var(--md-sys-color-on-surface);
}
.modal-header .btn-close {
margin: 0;
}
/* Primary btn */
.btn-primary {
color: var(--md-sys-color-on-primary);
background-color: var(--md-sys-color-primary);
border-color: var(--md-sys-color-primary);
box-shadow: none !important;
}
.btn-primary.disabled,
.btn-primary:disabled {
color: var(--md-sys-color-on-primary);
background-color: var(--md-sys-color-primary);
border-color: var(--md-sys-color-primary);
}
.btn-primary:hover {
color: var(--md-sys-color-on-primary);
background-color: var(--md-sys-color-primary);
border-color: var(--md-sys-color-primary);
box-shadow: var(--md-sys-elevation-3) !important;
}
.btn-check:active+.btn-primary,
.btn-check:checked+.btn-primary,
.btn-primary.active,
.btn-primary:active,
.show>.btn-primary.dropdown-toggle {
color: var(--md-sys-color-on-primary);
background-color: var(--md-sys-color-primary);
border-color: var(--md-sys-color-primary);
box-shadow: var(--md-sys-elevation-3) !important;
}
.btn-check:focus+.btn-primary,
.btn-primary:focus {
color: var(--md-sys-color-on-primary);
background-color: var(--md-sys-color-primary);
border-color: var(--md-sys-color-primary);
box-shadow: var(--md-sys-elevation-3) !important;
}
/* Secondary btn */
.btn-secondary {
color: var(--md-sys-color-on-secondary);
background-color: var(--md-sys-color-secondary);
border-color: var(--md-sys-color-secondary);
box-shadow: none !important;
}
.btn-secondary.disabled,
.btn-secondary:disabled {
color: var(--md-sys-color-on-secondary);
background-color: var(--md-sys-color-secondary);
border-color: var(--md-sys-color-secondary);
}
.btn-secondary:hover {
color: var(--md-sys-color-on-secondary);
background-color: var(--md-sys-color-secondary);
border-color: var(--md-sys-color-secondary);
box-shadow: var(--md-sys-elevation-3) !important;
}
.btn-check:active+.btn-secondary,
.btn-check:checked+.btn-secondary,
.btn-secondary.active,
.btn-secondary:active,
.show>.btn-secondary.dropdown-toggle {
color: var(--md-sys-color-on-secondary);
background-color: var(--md-sys-color-secondary);
border-color: var(--md-sys-color-secondary);
box-shadow: var(--md-sys-elevation-3) !important;
}
.btn-check:focus+.btn-secondary,
.btn-secondary:focus {
color: var(--md-sys-color-on-secondary);
background-color: var(--md-sys-color-secondary);
border-color: var(--md-sys-color-secondary);
box-shadow: var(--md-sys-elevation-3) !important;
}
/* Danger btn */
.btn-danger {
color: var(--md-sys-color-on-error);
background-color: var(--md-sys-color-error);
border-color: var(--md-sys-color-error);
box-shadow: none !important;
}
.btn-danger.disabled,
.btn-danger:disabled {
color: var(--md-sys-color-on-error);
background-color: var(--md-sys-color-error);
border-color: var(--md-sys-color-error);
}
.btn-danger:hover {
color: var(--md-sys-color-on-error);
background-color: var(--md-sys-color-error);
border-color: var(--md-sys-color-error);
box-shadow: var(--md-sys-elevation-3) !important;
}
.btn-check:active+.btn-danger,
.btn-check:checked+.btn-danger,
.btn-danger.active,
.btn-danger:active,
.show>.btn-danger.dropdown-toggle {
color: var(--md-sys-color-on-error);
background-color: var(--md-sys-color-error);
border-color: var(--md-sys-color-error);
box-shadow: var(--md-sys-elevation-3) !important;
}
.btn-check:focus+.btn-danger,
.btn-danger:focus {
color: var(--md-sys-color-on-error);
background-color: var(--md-sys-color-error);
border-color: var(--md-sys-color-error);
box-shadow: var(--md-sys-elevation-3) !important;
}
/* Info btn */
.btn-info {
color: var(--md-sys-color-on-tertiary);
background-color: var(--md-sys-color-tertiary);
border-color: var(--md-sys-color-tertiary);
box-shadow: none !important;
}
.btn-info .disabled,
.btn-info:disabled {
color: var(--md-sys-color-on-tertiary);
background-color: var(--md-sys-color-tertiary);
border-color: var(--md-sys-color-tertiary);
}
.btn-info:hover {
color: var(--md-sys-color-on-tertiary);
background-color: var(--md-sys-color-tertiary);
border-color: var(--md-sys-color-tertiary);
box-shadow: var(--md-sys-elevation-3) !important;
}
.btn-check:active+.btn-info,
.btn-check:checked+.btn-info,
.btn-info .active,
.btn-info:active,
.show>.btn-info.dropdown-toggle {
color: var(--md-sys-color-on-tertiary);
background-color: var(--md-sys-color-tertiary);
border-color: var(--md-sys-color-tertiary);
box-shadow: var(--md-sys-elevation-3) !important;
}
.btn-check:focus+.btn-info,
.btn-info:focus {
color: var(--md-sys-color-on-tertiary);
background-color: var(--md-sys-color-tertiary);
border-color: var(--md-sys-color-tertiary);
box-shadow: var(--md-sys-elevation-3) !important;
}
/* Info btn */
.btn-success {
box-shadow: none !important;
}
.btn-success:hover,
.btn-check:active+.btn-success,
.btn-check:checked+.btn-success,
.btn-success .active,
.btn-success:active,
.show>.btn-success.dropdown-toggle,
.btn-check:focus+.btn-success,
.btn-success:focus {
box-shadow: var(--md-sys-elevation-3) !important;
}
/* Warning btn */
.btn-warning {
box-shadow: none !important;
}
.btn-warning:hover,
.btn-check:active+.btn-warning,
.btn-check:checked+.btn-warning,
.btn-warning .active,
.btn-warning:active,
.show>.btn-warning.dropdown-toggle,
.btn-check:focus+.btn-warning,
.btn-warning:focus {
box-shadow: var(--md-sys-elevation-3) !important;
}
/* Outline Primary btn */
.btn-outline-primary {
color: var(--md-sys-color-primary);
background-color: transparent;
border-color: var(--md-sys-color-primary);
box-shadow: none !important;
}
.btn-outline-primary .disabled,
.btn-outline-primary:disabled {
color: var(--md-sys-color-primary);
background-color: transparent;
border-color: var(--md-sys-color-primary);
}
.btn-outline-primary:hover {
color: var(--md-sys-color-on-primary);
background-color: var(--md-sys-color-primary);
border-color: var(--md-sys-color-primary);
}
.btn-check:active+.btn-outline-primary,
.btn-check:checked+.btn-outline-primary,
.btn-outline-primary .active,
.btn-outline-primary:active,
.show>.btn-outline-primary.dropdown-toggle {
color: var(--md-sys-color-on-primary);
background-color: var(--md-sys-color-primary);
border-color: var(--md-sys-color-primary);
}
.btn-check:focus+.btn-outline-primary,
.btn-outline-primary:focus {
color: var(--md-sys-color-on-primary);
background-color: var(--md-sys-color-primary);
border-color: var(--md-sys-color-primary);
}
/* Outline Secondary btn */
.btn-outline-secondary {
color: var(--md-sys-color-secondary);
background-color: transparent;
border-color: var(--md-sys-color-secondary);
box-shadow: none !important;
}
.btn-outline-secondary .disabled,
.btn-outline-secondary:disabled {
color: var(--md-sys-color-secondary);
background-color: transparent;
border-color: var(--md-sys-color-secondary);
}
.btn-outline-secondary:hover {
color: var(--md-sys-color-on-secondary);
background-color: var(--md-sys-color-secondary);
border-color: var(--md-sys-color-secondary);
}
.btn-check:active+.btn-outline-secondary,
.btn-check:checked+.btn-outline-secondary,
.btn-outline-secondary .active,
.btn-outline-secondary:active,
.show>.btn-outline-secondary.dropdown-toggle {
color: var(--md-sys-color-on-secondary);
background-color: var(--md-sys-color-secondary);
border-color: var(--md-sys-color-secondary);
}
.btn-check:focus+.btn-outline-secondary,
.btn-outline-secondary:focus {
color: var(--md-sys-color-on-secondary);
background-color: var(--md-sys-color-secondary);
border-color: var(--md-sys-color-secondary);
}
/* Disabled btn */
.btn.disabled,
.btn:disabled,
fieldset:disabled .btn {
pointer-events: none;
opacity: 0.65;
}
/* Range Slider */
.form-range{
margin-top: 0.25rem;
}
.form-range:focus::-webkit-slider-thumb {
box-shadow: 0 0 0 1px var(--md-sys-color-surface), 0 0 0 .25rem var(--md-sys-color-primary)
}
.form-range:focus::-moz-range-thumb {
box-shadow: 0 0 0 1px var(--md-sys-color-surface), 0 0 0 .25rem var(--md-sys-color-primary)
}
.form-range::-webkit-slider-thumb {
background-color: var(--md-sys-color-primary);
}
.form-range::-webkit-slider-thumb:active {
background-color: var(--md-sys-color-primary)
}
.form-range::-webkit-slider-runnable-track {
background-color: var(--md-sys-color-on-primary)
}
.form-range::-moz-range-thumb {
background-color: var(--md-sys-color-primary);
}
/* checkbox */
.form-check {
margin-bottom: 1rem;
}
.form-check-label {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.form-check-input {
width: 1.5rem;
height: 1.5rem;
margin: 0;
background-color: var(--md-sys-color-surface);
border: 2px solid var(--md-sys-color-outline-variant);
}
.form-check-input:checked {
background-color: var(--md-sys-color-primary);
border-color: var(--md-sys-color-outline-variant);
border: none;
}
.form-check-input:focus {
border-color: var(--md-sys-color-outline-variant);
outline: 0;
box-shadow: 0 0 0 .25rem var(--md-sys-color-outline-variant);
}
.form-check-input:checked[type=checkbox] {
background-image: none;
}
.form-check input[type="checkbox"]:checked+span.material-symbols-rounded {
display: block;
}
.form-check span.material-symbols-rounded {
display: none;
color: var(--md-sys-color-surface);
position: absolute;
margin-left: -1.5rem;
margin-right: -1.5rem;
pointer-events: none;
user-select: none;
-webkit-user-select: none;
-webkit-tap-highlight-color: rgb(0 0 0 / 0%);
}
.form-check {
min-height: 22px;
padding-left: 0;
}
.form-check > label {
padding-left: 29px !important;
min-height: 22px;
line-height: 22px;
display: inline-block;
position: relative;
vertical-align: top;
margin-bottom: 0;
font-weight: normal;
cursor: pointer;
padding-right: 29px !important;
}
.form-check > input:first-child {
position: absolute !important;
opacity: 0;
margin: 0;
background-color: var(--md-sys-state-hover-opacity);
border-radius: 50%;
appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
-ms-appearance: none;
display: block;
width: 22px;
height: 22px;
outline: none;
transform: scale(1.65);
-ms-transform: scale(1.65);
transition: opacity .3s;
}
.form-check > input:first-child:hover {
opacity: 1;
transform: scale(1.65);
-ms-transform: scale(1.65);
}
.form-check > input:first-child:disabled {
cursor: default;
}
.form-check > input:first-child:disabled + label,
.form-check > input:first-child:disabled + input[type="hidden"] + label,
.form-check > input:first-child:disabled + label::before,
.form-check > input:first-child:disabled + input[type="hidden"] + label::before {
pointer-events: none;
cursor: default;
filter: alpha(opacity=65);
-webkit-box-shadow: none;
box-shadow: none;
opacity: .65;
}
.form-check > input:first-child + label::before,
.form-check > input:first-child + input[type="hidden"] + label::before {
content: "";
display: inline-block;
position: absolute;
width: 22px;
height: 22px;
border: 2px solid var(--md-sys-color-on-surface-variant);
border-radius: 3px;
margin-left: -29px;
box-sizing: border-box;
margin-right: -29px;
}
.form-check > input:first-child:checked + label::after,
.form-check > input:first-child:checked + input[type="hidden"] + label::after {
content: "";
display: inline-block;
position: absolute;
top: 0;
left: 0;
width: 7px;
height: 10px;
border: solid 2px;
border-left: none;
border-top: none;
transform: translate(7.75px, 4.5px) rotate(45deg);
-ms-transform: translate(7.75px, 4.5px) rotate(45deg);
box-sizing: border-box;
right: 0;
margin-right: 14px;
border-bottom-color: var(--md-sys-color-on-primary);
border-right-color: var(--md-sys-color-on-primary);
}
.form-check > input:first-child::-ms-check {
opacity: 0;
border-radius: 50%;
background-color: var(--md-sys-color-primary);
}
.form-check > input:first-child:active {
transform: scale(0);
-ms-transform: scale(0);
opacity: 1;
transition: opacity 0s, transform 0s;
}
.form-check > input[type="radio"]:first-child + label::before,
.form-check > input[type="radio"]:first-child + input[type="hidden"] + label::before {
border-radius: 50%;
}
.form-check > input[type="radio"]:first-child:checked + label::before,
.form-check > input[type="radio"]:first-child:checked + input[type="hidden"] + label::before {
background-color: transparent;
}
.form-check > input[type="radio"]:first-child:checked + label::after,
.form-check > input[type="radio"]:first-child:checked + input[type="hidden"] + label::after {
content: "";
position: absolute;
width: 10px;
height: 10px;
border-radius: 50%;
border: none;
top: 6px;
left: 6px;
transform: none;
-ms-transform: none;
}
.form-check > input[type="checkbox"]:first-child:checked + label::after,
.form-check > input[type="checkbox"]:first-child:checked + input[type="hidden"] + label::after {
width: 8px;
height: 14px;
transform: translate(7px, 2px) rotate(45deg);
-ms-transform: translate(7px, 2px) rotate(45deg);
}
.form-check-inline {
display: inline-block;
}
.form-check-inline + .form-check-inline {
margin-left: .75rem;
margin-top: 6px;
}
.form-check > input:first-child:checked + label::before,
.form-check > input:first-child:checked + input[type="hidden"] + label::before {
background-color: var(--md-sys-color-primary);
border-color: var(--md-sys-color-primary);
}
/* Forms */
textarea.form-control {
border-radius: 1.5rem !important;
}
.form-control,
.form-select,
.form-control:disabled,
.form-control[readonly] {
color: var(--md-sys-color-on-surface);
background-color: var(--md-sys-color-surface-container-low);
border-color: var(--md-sys-color-outline-variant);
border-radius: 3rem !important;
}
.form-control:focus,
.form-select:focus {
color: var(--md-sys-color-on-surface);
background-color: var(--md-sys-color-surface-container-lowest);
border-color: var(--md-sys-color-outline-variant);
outline: 0;
box-shadow: 0 0 0 0.25rem var(--md-sys-color-outline-variant);
}
.form-control-color {
padding: 0;
height: 2.4rem;
width: 2.4rem;
}
.form-control input[type="color"] {
opacity: 0;
height: 2.4rem;
width: 2.4rem;
box-sizing: border-box;
}
.form-control input[type="color"]:hover{
cursor: pointer;
}
/* Navbar Components */
.navbar-brand {
color: var(--md-sys-color-on-surface) !important;
}
.nav-link {
display: flex;
align-items: center;
transition: none !important;
padding: 0.5rem 1rem !important;
border: 1px transparent;
}
.navbar-nav li {
flex: 1;
}
.navbar-nav .nav-link {
color: var(--md-sys-color-on-surface-variant);
}
.navbar-nav .nav-link:focus,
.navbar-nav .nav-link:hover {
color: var(--md-sys-color-on-secondary-container);
background-color: var(--md-sys-color-surface-3);
border-radius: 3rem;
font-weight: 500;
font-variation-settings: var(--md-sys-icon-fill-1);
}
.navbar-nav .nav-link.active,
.navbar-nav .show>.nav-link {
color: var(--md-sys-color-on-secondary-container);
background-color: var(--md-sys-color-surface-5);
border-radius: 3rem;
font-weight: 500;
font-variation-settings: var(--md-sys-icon-fill-1);
}
.menu-title {
padding: 0 1rem;
}
.dropdown-menu {
margin: 0 1%;
padding: 1.5rem 0;
border-radius: 1rem;
min-width: 20rem;
color: var(--md-sys-color-on-surface);
background-color: var(--md-sys-color-surface-container);
border: 1px solid var(--md-sys-color-surface-5);
box-shadow: var(--md-sys-elevation-2);
}
.dropdown-item {
color: var(--md-sys-color-on-surface);
padding: 0.25rem 1rem;
border-radius: 3rem;
}
.dropdown-item:focus,
.dropdown-item:hover {
color: var(--md-sys-color-on-surface);
background-color: var(--md-sys-color-surface-5);
border-radius: 3rem;
font-variation-settings: var(--md-sys-icon-fill-1);
}
.dropdown-item.no-hover:hover,
.dropdown-item.no-hover:focus {
color: var(--md-sys-color-on-surface) !important;
background-color: transparent !important;
border-radius: 3rem !important;
font-variation-settings: initial !important;
}
.dropdown-item.active,
.dropdown-item:active {
color: var(--md-sys-color-on-surface);
background-color: var(--md-sys-color-surface-5);
border-radius: 3rem;
font-weight: 500;
font-variation-settings: var(--md-sys-icon-fill-1);
}
/* list-group-item */
.list-group-item {
color: var(--md-sys-color-on-surface);
background-color: var(--md-sys-color-surface-5);
border: 1px solid var(--md-sys-color-outline-variant);
}
.list-group-item:first-child {
border-radius: 1rem 1rem 0rem 0rem;
}
.list-group-item:last-child {
border-radius: 0rem 0rem 1rem 1rem;
}
.list-group-item:only-child {
border-radius: 1rem 1rem 1rem 1rem;
}
.list-group-item .btn {
padding: .375rem .5rem;
}
/*Alert */
.alert-container {
padding: 2rem 3rem;
border-radius: 3rem;
margin: 1rem 0rem 2rem;
}
.alert-header {
display: flex !important;
justify-content: space-between;
}
.alert-heading {
font-size: calc(1.275rem + .3vw);
}
.alert-dismissible .btn-close {
position: relative;
padding: 0;
}
.alert-danger {
color: var(--md-sys-color-on-error-container);
background-color: var(--md-sys-color-error-container);
border-color: transparent;
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16"><g clip-path="url(#clip0_1_37)"><path fill="#fff" d="M13 0H6C5.46957 0 4.96086 0.210714 4.58579 0.585786C4.21071 0.960859 4 1.46957 4 2C3.46957 2 2.96086 2.21071 2.58579 2.58579C2.21071 2.96086 2 3.46957 2 4V14C2 14.5304 2.21071 15.0391 2.58579 15.4142C2.96086 15.7893 3.46957 16 4 16H11C11.5304 16 12.0391 15.7893 12.4142 15.4142C12.7893 15.0391 13 14.5304 13 14C13.5304 14 14.0391 13.7893 14.4142 13.4142C14.7893 13.0391 15 12.5304 15 12V2C15 1.46957 14.7893 0.960859 14.4142 0.585786C14.0391 0.210714 13.5304 0 13 0ZM13 13V4C13 3.46957 12.7893 2.96086 12.4142 2.58579C12.0391 2.21071 11.5304 2 11 2H5C5 1.73478 5.10536 1.48043 5.29289 1.29289C5.48043 1.10536 5.73478 1 6 1H13C13.2652 1 13.5196 1.10536 13.7071 1.29289C13.8946 1.48043 14 1.73478 14 2V12C14 12.2652 13.8946 12.5196 13.7071 12.7071C13.5196 12.8946 13.2652 13 13 13ZM3 4C3 3.73478 3.10536 3.48043 3.29289 3.29289C3.48043 3.10536 3.73478 3 4 3H11C11.2652 3 11.5196 3.10536 11.7071 3.29289C11.8946 3.48043 12 3.73478 12 4V14C12 14.2652 11.8946 14.5196 11.7071 14.7071C11.5196 14.8946 11.2652 15 11 15H4C3.73478 15 3.48043 14.8946 3.29289 14.7071C3.10536 14.5196 3 14.2652 3 14V4Z"/></g><defs><clipPath id="clip0_1_37"><rect width="16" height="16" fill="#fff"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-book" viewBox="0 0 16 16"><path d="M1 2.828c.885-.37 2.154-.769 3.388-.893 1.33-.134 2.458.063 3.112.752v9.746c-.935-.53-2.12-.603-3.213-.493-1.18.12-2.37.461-3.287.811zm7.5-.141c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783"/></svg>

After

Width:  |  Height:  |  Size: 766 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21,10.12H14.22L16.96,7.3C14.23,4.6 9.81,4.5 7.08,7.2C4.35,9.91 4.35,14.28 7.08,17C9.81,19.7 14.23,19.7 16.96,17C18.32,15.65 19,14.08 19,12.1H21C21,14.08 20.12,16.65 18.36,18.39C14.85,21.87 9.15,21.87 5.64,18.39C2.14,14.92 2.11,9.28 5.62,5.81C9.13,2.34 14.76,2.34 18.27,5.81L21,3V10.12M12.5,8V12.25L16,14.33L15.28,15.54L11,13V8H12.5Z"/></svg>

After

Width:  |  Height:  |  Size: 411 B

View File

@ -0,0 +1,50 @@
var traceVisible = false;
function toggletrace() {
var traceDiv = document.getElementById("trace");
if (!traceVisible) {
traceDiv.style.maxHeight = "500px";
traceVisible = true;
} else {
traceDiv.style.maxHeight = "0px";
traceVisible = false;
}
adjustContainerHeight();
}
function copytrace() {
var flip = false;
if (!traceVisible) {
toggletrace();
flip = true;
}
var traceContent = document.getElementById("traceContent");
var range = document.createRange();
range.selectNode(traceContent);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand("copy");
window.getSelection().removeAllRanges();
if (flip) {
toggletrace();
}
}
function dismissError() {
var errorContainer = document.getElementById("errorContainer");
errorContainer.style.display = "none";
errorContainer.style.height = "0";
}
function adjustContainerHeight() {
var errorContainer = document.getElementById("errorContainer");
var traceDiv = document.getElementById("trace");
if (traceVisible) {
errorContainer.style.height = errorContainer.scrollHeight - traceDiv.scrollHeight + traceDiv.offsetHeight + "px";
} else {
errorContainer.style.height = "auto";
}
}
function showHelp() {
$("#helpModal").modal("show");
}

View File

@ -0,0 +1,245 @@
function filterCards() {
var input = document.getElementById('searchBar');
var filter = input.value.toUpperCase().trim();
// Split the input filter into individual words for multi-word matching
var filterWords = filter.split(/[\s,;.\-]+/);
let featureGroups = document.querySelectorAll('.feature-group');
for (const featureGroup of featureGroups) {
var cards = featureGroup.querySelectorAll('.dropdown-item');
let groupMatchesFilter = false;
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
var title = card.getAttribute('title') || '';
// Get the navbar tags associated with the card
var navbarItem = document.querySelector(`a.dropdown-item[href="${card.id}"]`);
var navbarTags = navbarItem ? navbarItem.getAttribute('data-bs-tags') : '';
navbarTags = navbarItem ? navbarTags + ',' + navbarItem.getAttribute('data-bs-title') + ',' + navbarItem.children[0].getAttribute('data-title') : navbarTags;
var content = (title + ' ' + navbarTags).toUpperCase();
// Check if all words in the filter match the content
var matches = filterWords.every((word) => content.includes(word));
if (matches) {
card.style.display = '';
groupMatchesFilter = true;
} else {
card.style.display = 'none';
}
}
if (!groupMatchesFilter) {
featureGroup.style.display = 'none';
} else {
featureGroup.style.display = '';
}
}
}
function updateFavoritesSection() {
const favoritesContainer = document.getElementById('groupFavorites').querySelector('.nav-group-container');
favoritesContainer.innerHTML = '';
let favoritesAmount = 0;
const favouritesList = JSON.parse(localStorage.getItem('favoritesList') || '[]');
const isFavoritesView = JSON.parse(localStorage.getItem('favoritesView') || 'false');
favouritesList.forEach((value) => {
var navbarEntry = document.querySelector(`a[data-bs-link='${value}']`);
if (navbarEntry) {
const duplicate = navbarEntry.cloneNode(true);
favoritesContainer.appendChild(duplicate);
}
favoritesAmount++;
});
if (favoritesAmount === 0 || !isFavoritesView) {
document.getElementById('groupFavorites').style.display = 'none';
} else {
document.getElementById('groupFavorites').style.display = 'flex';
}
reorderCards(favoritesContainer);
//favoritesContainer.style.maxHeight = favoritesContainer.scrollHeight + 'px';
}
function reorderCards(container) {
var cards = Array.from(container.querySelectorAll('.dropdown-item'));
cards.forEach(function (card) {
container.removeChild(card);
});
cards.sort(function (a, b) {
var aIsFavorite = localStorage.getItem(a.id) === 'favorite';
var bIsFavorite = localStorage.getItem(b.id) === 'favorite';
if (aIsFavorite && !bIsFavorite) {
return -1;
} else if (!aIsFavorite && bIsFavorite) {
return 1;
} else {
return a.id > b.id;
}
});
cards.forEach(function (card) {
container.appendChild(card);
});
}
function initializeCards() {
updateFavoritesSection();
updateFavoritesView();
updateFavoritesDropdown();
filterCards();
}
function updateFavoritesView() {
const isFavoritesView = JSON.parse(localStorage.getItem('favoritesView') || 'false');
const iconElement = document.getElementById('toggle-favourites-icon');
const favoritesGroup = document.querySelector('#groupFavorites');
const favoritesList = JSON.parse(localStorage.getItem('favoritesList')) || [];
if (isFavoritesView && favoritesList.length > 0) {
document.getElementById('favouritesVisibility').style.display = 'flex';
favoritesGroup.style.display = 'flex';
} else {
if (favoritesList.length > 0) {
document.getElementById('favouritesVisibility').style.display = 'flex';
favoritesGroup.style.display = 'none';
} else {
document.getElementById('favouritesVisibility').style.display = 'none';
}
}
}
function toggleFavoritesMode() {
const favoritesMode = !document.querySelector('.toggle-favourites').classList.contains('active');
document.querySelector('.toggle-favourites').classList.toggle('active', favoritesMode);
document.querySelectorAll('.favorite-icon').forEach((icon) => {
const endpoint = icon.getAttribute('data-endpoint');
const parent = icon.closest('.dropdown-item');
const isInGroupRecent = parent.closest('#groupRecent') !== null;
const isInGroupFavorites = parent.closest('#groupFavorites') !== null;
if (isInGroupRecent) {
icon.style.display = 'none';
} else if (isInGroupFavorites) {
icon.style.display = favoritesMode ? 'inline-block' : 'none';
icon.textContent = 'close_small';
} else {
icon.style.display = favoritesMode ? 'inline-block' : 'none';
const favoritesList = JSON.parse(localStorage.getItem('favoritesList')) || [];
icon.textContent = favoritesList.includes(endpoint) ? 'close_small' : 'add';
}
});
document.querySelectorAll('.dropdown-item').forEach((link) => {
if (favoritesMode) {
link.dataset.originalHref = link.getAttribute('href');
link.setAttribute('href', '#');
link.classList.add('no-hover');
} else {
link.setAttribute('href', link.dataset.originalHref || '#');
link.classList.remove('no-hover');
}
});
const isFavoritesView = JSON.parse(localStorage.getItem('favoritesView') || 'false');
if (favoritesMode && !isFavoritesView) {
toggleFavoritesView();
}
}
function toggleFavoritesView() {
const isFavoritesView = JSON.parse(localStorage.getItem('favoritesView') || 'false');
localStorage.setItem('favoritesView', !isFavoritesView);
updateFavoritesView();
}
window.onload = function () {
initializeCards();
};
function sortNavElements(criteria) {
document.querySelectorAll('.nav-group-container').forEach((container) => {
const items = Array.from(container.children);
items.sort((a, b) => {
if (criteria === 'alphabetical') {
const titleA = a.querySelector('.icon-text')?.textContent.trim().toLowerCase() || '';
const titleB = b.querySelector('.icon-text')?.textContent.trim().toLowerCase() || '';
return titleA.localeCompare(titleB);
} else if (criteria === 'global') {
const popularityA = parseInt(a.dataset.popularity, 10) || 1000;
const popularityB = parseInt(b.dataset.popularity, 10) || 1000;
return popularityA - popularityB;
}
return 0;
});
container.innerHTML = '';
items.forEach((item) => container.appendChild(item));
});
}
async function fetchPopularityData(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.text();
}
function applyPopularityData(popularityData) {
document.querySelectorAll('.dropdown-item').forEach((item) => {
const endpoint = item.getAttribute('data-bs-link');
const popularity = popularityData['/' + endpoint];
if (endpoint && popularity !== undefined) {
item.setAttribute('data-popularity', popularity);
}
});
const currentSort = localStorage.getItem('homepageSort') || 'alphabetical';
const sortDropdown = document.getElementById('sort-options');
if (sortDropdown) {
sortDropdown.value = currentSort;
``;
}
sortNavElements(currentSort);
}
document.addEventListener('DOMContentLoaded', async function () {
const sortDropdown = document.getElementById('sort-options');
if (sortDropdown) {
sortDropdown.addEventListener('change', (event) => {
const selectedOption = event.target.value;
localStorage.setItem('homepageSort', selectedOption);
sortNavElements(selectedOption);
});
}
try {
const response = await fetch('files/popularity.txt');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const popularityData = await response.json();
applyPopularityData(popularityData);
} catch (error) {
console.error('Error loading popularity data:', error);
}
const materialIcons = new FontFaceObserver('Material Symbols Rounded');
materialIcons
.load()
.then(() => {
document.querySelectorAll('.dropdown-item.hidden').forEach((el) => {
el.classList.remove('hidden');
});
})
.catch(() => {
console.error('Material Symbols Rounded font failed to load.');
});
});

View File

@ -4,10 +4,21 @@ document.addEventListener('DOMContentLoaded', function () {
if (window.analyticsPromptBoolean) { if (window.analyticsPromptBoolean) {
const analyticsModal = new bootstrap.Modal(document.getElementById('analyticsModal')); const analyticsModal = new bootstrap.Modal(document.getElementById('analyticsModal'));
analyticsModal.show(); analyticsModal.show();
let retryCount = 0;
function hideCookieBanner() {
const cookieBanner = document.querySelector('#cc-main');
if (cookieBanner && cookieBanner.offsetHeight > 0) {
cookieBanner.style.display = "none";
} else if (retryCount < 20) {
retryCount++;
setTimeout(hideCookieBanner, 100);
}
}
hideCookieBanner();
} }
}); });
/*]]>*/ /*]]>*/function setAnalytics(enabled) {
function setAnalytics(enabled) {
fetchWithCsrf('api/v1/settings/update-enable-analytics', { fetchWithCsrf('api/v1/settings/update-enable-analytics', {
method: 'POST', method: 'POST',
headers: { headers: {
@ -19,6 +30,15 @@ function setAnalytics(enabled) {
if (response.status === 200) { if (response.status === 200) {
console.log('Analytics setting updated successfully'); console.log('Analytics setting updated successfully');
bootstrap.Modal.getInstance(document.getElementById('analyticsModal')).hide(); bootstrap.Modal.getInstance(document.getElementById('analyticsModal')).hide();
if (typeof CookieConsent !== "undefined") {
if (enabled) {
CookieConsent.acceptCategory(['analytics']);
} else {
CookieConsent.acceptCategory([]);
}
}
} else if (response.status === 208) { } else if (response.status === 208) {
console.log('Analytics setting has already been set. Please edit /config/settings.yml to change it.', response); console.log('Analytics setting has already been set. Please edit /config/settings.yml to change it.', response);
alert('Analytics setting has already been set. Please edit /config/settings.yml to change it.'); alert('Analytics setting has already been set. Please edit /config/settings.yml to change it.');

View File

@ -0,0 +1,111 @@
window.onload = function () {
var items = document.querySelectorAll(".dropdown-item, .nav-link");
var dummyContainer = document.createElement("div");
dummyContainer.style.position = "absolute";
dummyContainer.style.visibility = "hidden";
dummyContainer.style.whiteSpace = "nowrap"; // Ensure we measure full width
document.body.appendChild(dummyContainer);
var maxWidth = 0;
items.forEach(function (item) {
var clone = item.cloneNode(true);
dummyContainer.appendChild(clone);
var width = clone.offsetWidth;
if (width > maxWidth) {
maxWidth = width;
}
dummyContainer.removeChild(clone);
});
document.body.removeChild(dummyContainer);
// Store max width for later use
window.navItemMaxWidth = maxWidth;
};
document.querySelector("#navbarSearchInput").addEventListener("input", function (e) {
var searchText = e.target.value.trim().toLowerCase();
var items = document.querySelectorAll("a.dropdown-item[data-bs-tags]");
var resultsBox = document.querySelector("#searchResults");
resultsBox.innerHTML = "";
if (searchText !== "") {
var addedResults = new Set();
items.forEach(function (item) {
var titleElement = item.querySelector(".icon-text");
var iconElement = item.querySelector(".material-symbols-rounded, .icon");
var itemHref = item.getAttribute("href");
var tags = item.getAttribute("data-bs-tags") || "";
if (titleElement && iconElement && itemHref !== "#") {
var title = titleElement.innerText.trim();
if (
(title.toLowerCase().includes(searchText) || tags.toLowerCase().includes(searchText)) &&
!addedResults.has(itemHref)
) {
var dropdownItem = document.createElement("div");
dropdownItem.className = "dropdown-item d-flex justify-content-between align-items-center";
var contentWrapper = document.createElement("div");
contentWrapper.className = "d-flex align-items-center flex-grow-1";
contentWrapper.style.textDecoration = "none";
contentWrapper.style.color = "inherit";
var originalContent = item.querySelector("div").cloneNode(true);
contentWrapper.appendChild(originalContent);
contentWrapper.onclick = function () {
window.location.href = itemHref;
};
dropdownItem.appendChild(contentWrapper);
resultsBox.appendChild(dropdownItem);
addedResults.add(itemHref);
}
}
});
}
resultsBox.style.width = window.navItemMaxWidth + "px";
});
const searchDropdown = document.getElementById('searchDropdown');
const searchInput = document.getElementById('navbarSearchInput');
const dropdownMenu = searchDropdown.querySelector('.dropdown-menu');
// Handle dropdown shown event
searchDropdown.addEventListener('shown.bs.dropdown', function () {
searchInput.focus();
});
// Handle hover opening
searchDropdown.addEventListener('mouseenter', function () {
const dropdownInstance = new bootstrap.Dropdown(searchDropdown);
dropdownInstance.show();
setTimeout(() => {
searchInput.focus();
}, 100);
});
// Handle mouse leave
searchDropdown.addEventListener('mouseleave', function () {
// Check if current value is empty (including if user typed and then deleted)
if (searchInput.value.trim().length === 0) {
searchInput.blur();
const dropdownInstance = new bootstrap.Dropdown(searchDropdown);
dropdownInstance.hide();
}
});
searchDropdown.addEventListener('hidden.bs.dropdown', function () {
if (searchInput.value.trim().length === 0) {
searchInput.blur();
}
});

View File

@ -9,7 +9,7 @@ TabContainer = {
tabList.classList.add('tab-buttons'); tabList.classList.add('tab-buttons');
tabTitles.forEach((title) => { tabTitles.forEach((title) => {
const tabButton = document.createElement('button'); const tabButton = document.createElement('button');
tabButton.innerHTML = title; tabButton.textContent = title;
tabButton.onclick = (e) => { tabButton.onclick = (e) => {
this.setActiveTab(e.target); this.setActiveTab(e.target);
}; };

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,4 @@
àRCopyright 1990-2009 Adobe Systems Incorporated.
All rights reserved.
See ./LICENSE!!<21>º]aX!!]`<60>21<32>> <09>p <0B>z<EFBFBD>$]<06>"Rd<E2809A>-Uƒ7<C692>*4„%<25>+ „Z „{<7B>/%…<<3C>9K…b<E280A6>1]†.<2E>" ‰`]‡,<2C>"]ˆ
<EFBFBD>"]ˆh<CB86>"]‰F<E280B0>"]Š$<24>"]<02>"]`<60>"]Œ><3E>"]<5D><1C>"]<5D>z<EFBFBD>"]ŽX<C5BD>"]<5D>6<EFBFBD>"]<5D><14>"]<5D>r<EFBFBD>"]P<E28098>"].<2E>"]“ <0C>"]“j<E2809C>"]”H<E2809D>"]•&<26>"]<04>"]b<E28093>"]—@<40>"]˜<1E>"]˜|<7C>"]™Z<E284A2>"]š8<C5A1>"]<16>"]t<E280BA>"]œR<C593>"]<5D>0<EFBFBD>"]ž<0E>"]žl<C5BE>"]ŸJ<C5B8>"] (<28>"]¡<06>"]¡d<C2A1>"]¢B<C2A2>"]£ <20>"X£~<7E>']¤W<C2A4>"]¥5<C2A5>"]¦<13>"]¦q<C2A6>"]§O<C2A7>"]¨-<2D>"]© <0B>"]©i<C2A9>"]ªG<C2AA>"]«%<25>"]¬<03>"]¬a<C2AC>"]­?<3F>"]®<1D>"]®{<7B>"]¯Y<C2AF>"]°7<C2B0>"]±<15>"]±s<C2B1>"]²Q<C2B2>"]³/<2F>"]´ <0A>"]´k<C2B4>"]µI<C2B5>"]¶'<27>"]·<05>"]·c<C2B7>"]¸A<C2B8>"]¹<1F>"]¹}<7D>"]º[<5B>"]»9

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="13" fill="none" viewBox="0 0 12 13"><path fill="#FBFBFE" d="M6 0.5C5.21207 0.5 4.43185 0.655195 3.7039 0.956723C2.97595 1.25825 2.31451 1.70021 1.75736 2.25736C1.20021 2.81451 0.758251 3.47595 0.456723 4.2039C0.155195 4.93185 0 5.71207 0 6.5C0 7.28793 0.155195 8.06815 0.456723 8.7961C0.758251 9.52405 1.20021 10.1855 1.75736 10.7426C2.31451 11.2998 2.97595 11.7417 3.7039 12.0433C4.43185 12.3448 5.21207 12.5 6 12.5C7.5913 12.5 9.11742 11.8679 10.2426 10.7426C11.3679 9.61742 12 8.0913 12 6.5C12 4.9087 11.3679 3.38258 10.2426 2.25736C9.11742 1.13214 7.5913 0.5 6 0.5ZM5.06 8.9L2.9464 6.7856C2.85273 6.69171 2.80018 6.56446 2.80033 6.43183C2.80048 6.29921 2.85331 6.17207 2.9472 6.0784C3.04109 5.98473 3.16834 5.93218 3.30097 5.93233C3.43359 5.93248 3.56073 5.98531 3.6544 6.0792L5.3112 7.7368L8.3464 4.7008C8.44109 4.6109 8.56715 4.56153 8.69771 4.56322C8.82827 4.56492 8.95301 4.61754 9.04534 4.70986C9.13766 4.80219 9.19028 4.92693 9.19198 5.05749C9.19367 5.18805 9.1443 5.31411 9.0544 5.4088L5.5624 8.9H5.06Z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><rect style="fill:#ff0;fill-opacity:1;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" width="33.76" height="33.76" x="3.12" y="3.12"/><path d="m 20.677967,8.54499 c -7.342801,0 -13.295293,4.954293 -13.295293,11.065751 0,2.088793 0.3647173,3.484376 1.575539,5.150563 L 6.0267418,31.45501 13.560595,29.011117 c 2.221262,1.387962 4.125932,1.665377 7.117372,1.665377 7.3428,0 13.295291,-4.954295 13.295291,-11.065753 0,-6.111458 -5.952491,-11.065751 -13.295291,-11.065751 z" style="fill:#fff;fill-opacity:1;stroke:#000;stroke-width:.93031836;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/></svg>

After

Width:  |  Height:  |  Size: 745 B

Some files were not shown because too many files have changed in this diff Show More