mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-23 16:05:09 +00:00
Compare commits
9 Commits
a5479e76d7
...
7ef56889ce
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7ef56889ce | ||
![]() |
c745d4f290 | ||
![]() |
0523470c35 | ||
![]() |
b570a5ad14 | ||
![]() |
58c1bccfcc | ||
![]() |
75ec5e00ab | ||
![]() |
b500236733 | ||
![]() |
66cebc7f9f | ||
![]() |
f9df824705 |
14
.gitattributes
vendored
14
.gitattributes
vendored
@ -1,10 +1,10 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
# Ignore all JavaScript files in a directory
|
||||
src/main/resources/static/pdfjs/* linguist-vendored
|
||||
src/main/resources/static/pdfjs/** linguist-vendored
|
||||
src/main/resources/static/pdfjs-legacy/* linguist-vendored
|
||||
src/main/resources/static/pdfjs-legacy/** linguist-vendored
|
||||
src/main/resources/static/css/bootstrap-icons.css linguist-vendored
|
||||
src/main/resources/static/css/bootstrap.min.css linguist-vendored
|
||||
src/main/resources/static/css/fonts/* linguist-vendored
|
||||
stirling-pdf/src/main/resources/static/pdfjs/* linguist-vendored
|
||||
stirling-pdf/src/main/resources/static/pdfjs/** linguist-vendored
|
||||
stirling-pdf/src/main/resources/static/pdfjs-legacy/* linguist-vendored
|
||||
stirling-pdf/src/main/resources/static/pdfjs-legacy/** linguist-vendored
|
||||
stirling-pdf/src/main/resources/static/css/bootstrap-icons.css linguist-vendored
|
||||
stirling-pdf/src/main/resources/static/css/bootstrap.min.css linguist-vendored
|
||||
stirling-pdf/src/main/resources/static/css/fonts/* linguist-vendored
|
||||
|
57
.github/labeler-config.yml
vendored
57
.github/labeler-config.yml
vendored
@ -1,60 +1,45 @@
|
||||
Translation:
|
||||
- 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: 'src/main/resources/templates/fragments/languages.html'
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/resources/templates/fragments/languages.html'
|
||||
|
||||
Front End:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'src/main/resources/templates/**/*'
|
||||
- any-glob-to-any-file: 'src/main/resources/static/**/*'
|
||||
- any-glob-to-any-file: '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/resources/templates/**/*'
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/resources/static/**/*'
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/controller/web/**'
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/UI/**/*'
|
||||
|
||||
Java:
|
||||
- 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:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/**/*'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/**/*'
|
||||
- any-glob-to-any-file: 'src/main/resources/settings.yml.template'
|
||||
- any-glob-to-any-file: 'src/main/resources/application.properties'
|
||||
- any-glob-to-any-file: 'src/main/resources/banner.txt'
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/config/**/*'
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/controller/**/*'
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/resources/settings.yml.template'
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/resources/application.properties'
|
||||
- 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: 'split_photos.py'
|
||||
|
||||
Security:
|
||||
- changed-files:
|
||||
- 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/**/*'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/DatabaseController.java'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/EmailController.java'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/H2SQLController.java'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/DatabaseWebController.java'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/UserController.java'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/api/Email.java'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/exception/BackupNotFoundException.java'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/exception/NoProviderFoundExceptionjava'
|
||||
- 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/ApiKeyAuthenticationToken.java'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/AttemptCounter.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: 'proprietary/src/main/java/stirling/software/proprietary/security/**/*'
|
||||
- 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/scorecards.yml'
|
||||
|
||||
API:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '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: '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/config/OpenApiConfig.java'
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/controller/web/MetricsController.java'
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/java/stirling/software/SPDF/controller/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: 'split_photos.py'
|
||||
- any-glob-to-any-file: '.github/workflows/swagger.yml'
|
||||
@ -88,7 +73,9 @@ Devtools:
|
||||
Test:
|
||||
- changed-files:
|
||||
- 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: '.pre-commit-config'
|
||||
- any-glob-to-any-file: '.github/workflows/pre_commit.yml'
|
||||
|
2
.github/scripts/check_language_properties.py
vendored
2
.github/scripts/check_language_properties.py
vendored
@ -317,7 +317,7 @@ def check_for_differences(reference_file, file_list, branch, actor):
|
||||
report.append("## ❌ Overall Check Status: **_Failed_**")
|
||||
report.append("")
|
||||
report.append(
|
||||
f"@{actor} please check your translation if it conforms to the standard. Follow the format of [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties)"
|
||||
f"@{actor} please check your translation if it conforms to the standard. Follow the format of [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/stirling-pdf/src/main/resources/messages_en_GB.properties)"
|
||||
)
|
||||
else:
|
||||
report.append("## ✅ Overall Check Status: **_Success_**")
|
||||
|
25
.github/workflows/check_properties.yml
vendored
25
.github/workflows/check_properties.yml
vendored
@ -4,7 +4,7 @@ on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "src/main/resources/messages_*.properties"
|
||||
- "stirling-pdf/src/main/resources/messages_*.properties"
|
||||
|
||||
permissions:
|
||||
contents: read # Allow read access to repository content
|
||||
@ -61,7 +61,20 @@ jobs:
|
||||
run: |
|
||||
echo "Fetching PR changed files..."
|
||||
echo "Getting list of changed files from PR..."
|
||||
gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$' > changed_files.txt # Filter only matching property files
|
||||
# Check if PR number exists
|
||||
if [ -z "${{ steps.get-pr-data.outputs.pr_number }}" ]; then
|
||||
echo "Error: PR number is empty"
|
||||
exit 1
|
||||
fi
|
||||
# Get changed files and filter for properties files, handle case where no matches are found
|
||||
gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^stirling-pdf/src/main/resources/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$' > changed_files.txt || echo "No matching properties files found in PR"
|
||||
# Check if any files were found
|
||||
if [ ! -s changed_files.txt ]; then
|
||||
echo "No properties files changed in this PR"
|
||||
echo "Workflow will exit early as no relevant files to check"
|
||||
exit 0
|
||||
fi
|
||||
echo "Found $(wc -l < changed_files.txt) matching properties files"
|
||||
|
||||
- name: Determine reference file test
|
||||
id: determine-file
|
||||
@ -103,7 +116,7 @@ jobs:
|
||||
// Filter for relevant files based on the PR changes
|
||||
const changedFiles = files
|
||||
.map(file => file.filename)
|
||||
.filter(file => /^src\/main\/resources\/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$/.test(file));
|
||||
.filter(file => /^stirling-pdf\src\/main\/resources\/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$/.test(file));
|
||||
|
||||
console.log("Changed files:", changedFiles);
|
||||
|
||||
@ -141,12 +154,12 @@ jobs:
|
||||
|
||||
// Determine reference file
|
||||
let referenceFilePath;
|
||||
if (changedFiles.includes("src/main/resources/messages_en_GB.properties")) {
|
||||
if (changedFiles.includes("stirling-pdf/src/main/resources/messages_en_GB.properties")) {
|
||||
console.log("Using PR branch reference file.");
|
||||
const { data: fileContent } = await github.rest.repos.getContent({
|
||||
owner: prRepoOwner,
|
||||
repo: prRepoName,
|
||||
path: "src/main/resources/messages_en_GB.properties",
|
||||
path: "stirling-pdf/src/main/resources/messages_en_GB.properties",
|
||||
ref: branch,
|
||||
});
|
||||
|
||||
@ -158,7 +171,7 @@ jobs:
|
||||
const { data: fileContent } = await github.rest.repos.getContent({
|
||||
owner: repoOwner,
|
||||
repo: repoName,
|
||||
path: "src/main/resources/messages_en_GB.properties",
|
||||
path: "stirling-pdf/src/main/resources/messages_en_GB.properties",
|
||||
ref: "main",
|
||||
});
|
||||
|
||||
|
4
.github/workflows/licenses-update.yml
vendored
4
.github/workflows/licenses-update.yml
vendored
@ -57,11 +57,11 @@ jobs:
|
||||
|
||||
- name: Move and rename license file
|
||||
run: |
|
||||
mv build/reports/dependency-license/index.json src/main/resources/static/3rdPartyLicenses.json
|
||||
mv build/reports/dependency-license/index.json stirling-pdf/src/main/resources/static/3rdPartyLicenses.json
|
||||
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git add src/main/resources/static/3rdPartyLicenses.json
|
||||
git add stirling-pdf/src/main/resources/static/3rdPartyLicenses.json
|
||||
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
|
||||
|
||||
- name: Create Pull Request
|
||||
|
10
.github/workflows/sync_files.yml
vendored
10
.github/workflows/sync_files.yml
vendored
@ -8,8 +8,8 @@ on:
|
||||
paths:
|
||||
- "build.gradle"
|
||||
- "README.md"
|
||||
- "src/main/resources/messages_*.properties"
|
||||
- "src/main/resources/static/3rdPartyLicenses.json"
|
||||
- "stirling-pdf/src/main/resources/messages_*.properties"
|
||||
- "stirling-pdf/src/main/resources/static/3rdPartyLicenses.json"
|
||||
- "scripts/ignore_translation.toml"
|
||||
|
||||
permissions:
|
||||
@ -41,11 +41,11 @@ jobs:
|
||||
|
||||
- name: Sync translation property files
|
||||
run: |
|
||||
python .github/scripts/check_language_properties.py --reference-file "src/main/resources/messages_en_GB.properties" --branch main
|
||||
python .github/scripts/check_language_properties.py --reference-file "stirling-pdf/src/main/resources/messages_en_GB.properties" --branch main
|
||||
|
||||
- name: Commit translation files
|
||||
run: |
|
||||
git add src/main/resources/messages_*.properties
|
||||
git add stirling-pdf/src/main/resources/messages_*.properties
|
||||
git diff --staged --quiet || git commit -m ":memo: Sync translation files" || echo "No changes detected"
|
||||
|
||||
- name: Install dependencies
|
||||
@ -101,4 +101,4 @@ jobs:
|
||||
sign-commits: true
|
||||
add-paths: |
|
||||
README.md
|
||||
src/main/resources/messages_*.properties
|
||||
stirling-pdf/src/main/resources/messages_*.properties
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -125,6 +125,9 @@ SwaggerDoc.json
|
||||
*.rar
|
||||
*.db
|
||||
/build
|
||||
/stirling-pdf/build
|
||||
/common/build
|
||||
/proprietary/build
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
@ -20,7 +20,7 @@ repos:
|
||||
- --skip="./.*,*.csv,*.json,*.ambr"
|
||||
- --quiet-level=2
|
||||
files: \.(html|css|js|py|md)$
|
||||
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile|.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js)
|
||||
exclude: (.vscode|.devcontainer|stirling-pdf/src/main/resources|Dockerfile|.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js)
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.26.0
|
||||
hooks:
|
||||
|
@ -137,9 +137,9 @@ services:
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /stirling/latest/data:/usr/share/tessdata:rw
|
||||
- /stirling/latest/config:/configs:rw
|
||||
- /stirling/latest/logs:/logs:rw
|
||||
- ./stirling/latest/data:/usr/share/tessdata:rw
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "false"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
@ -332,7 +332,7 @@ Thymeleaf is a server-side Java HTML template engine. It is used in Stirling-PDF
|
||||
|
||||
### Thymeleaf overview
|
||||
|
||||
In Stirling-PDF, Thymeleaf is used to create HTML templates that are rendered on the server side. These templates are located in the `src/main/resources/templates` directory. Thymeleaf templates use a combination of HTML and special Thymeleaf attributes to dynamically generate content.
|
||||
In Stirling-PDF, Thymeleaf is used to create HTML templates that are rendered on the server side. These templates are located in the `stirling-pdf/src/main/resources/templates` directory. Thymeleaf templates use a combination of HTML and special Thymeleaf attributes to dynamically generate content.
|
||||
|
||||
Some examples of this are:
|
||||
|
||||
@ -384,7 +384,7 @@ This would generate n entries of tr for each person in exampleData
|
||||
### Adding a New Feature to the Backend (API)
|
||||
|
||||
1. **Create a New Controller:**
|
||||
- Create a new Java class in the `src/main/java/stirling/software/SPDF/controller/api` directory.
|
||||
- Create a new Java class in the `stirling-pdf/src/main/java/stirling/software/SPDF/controller/api` directory.
|
||||
- Annotate the class with `@RestController` and `@RequestMapping` to define the API endpoint.
|
||||
- Ensure to add API documentation annotations like `@Tag(name = "General", description = "General APIs")` and `@Operation(summary = "Crops a PDF document", description = "This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")`.
|
||||
|
||||
@ -411,7 +411,7 @@ This would generate n entries of tr for each person in exampleData
|
||||
```
|
||||
|
||||
2. **Define the Service Layer:** (Not required but often useful)
|
||||
- Create a new service class in the `src/main/java/stirling/software/SPDF/service` directory.
|
||||
- Create a new service class in the `stirling-pdf/src/main/java/stirling/software/SPDF/service` directory.
|
||||
- Implement the business logic for the new feature.
|
||||
|
||||
```java
|
||||
@ -463,7 +463,7 @@ This would generate n entries of tr for each person in exampleData
|
||||
### Adding a New Feature to the Frontend (UI)
|
||||
|
||||
1. **Create a New Thymeleaf Template:**
|
||||
- Create a new HTML file in the `src/main/resources/templates` directory.
|
||||
- Create a new HTML file in the `stirling-pdf/src/main/resources/templates` directory.
|
||||
- Use Thymeleaf attributes to dynamically generate content.
|
||||
- Use `extract-page.html` as a base example for the HTML template, which is useful to ensure importing of the general layout, navbar, and footer.
|
||||
|
||||
@ -507,7 +507,7 @@ This would generate n entries of tr for each person in exampleData
|
||||
```
|
||||
|
||||
2. **Create a New Controller for the UI:**
|
||||
- Create a new Java class in the `src/main/java/stirling/software/SPDF/controller/ui` directory.
|
||||
- Create a new Java class in the `stirling-pdf/src/main/java/stirling/software/SPDF/controller/ui` directory.
|
||||
- Annotate the class with `@Controller` and `@RequestMapping` to define the UI endpoint.
|
||||
|
||||
```java
|
||||
@ -537,7 +537,7 @@ This would generate n entries of tr for each person in exampleData
|
||||
|
||||
3. **Update the Navigation Bar:**
|
||||
- Add a link to the new feature page in the navigation bar.
|
||||
- Update the `src/main/resources/templates/fragments/navbar.html` file.
|
||||
- Update the `stirling-pdf/src/main/resources/templates/fragments/navbar.html` file.
|
||||
|
||||
```html
|
||||
<li class="nav-item">
|
||||
@ -551,7 +551,7 @@ When adding a new feature or modifying existing ones in Stirling-PDF, you'll nee
|
||||
|
||||
### 1. Locate Existing Language Files
|
||||
|
||||
Find the existing `messages.properties` files in the `src/main/resources` directory. You'll see files like:
|
||||
Find the existing `messages.properties` files in the `stirling-pdf/src/main/resources` directory. You'll see files like:
|
||||
|
||||
- `messages.properties` (default, usually English)
|
||||
- `messages_en_GB.properties`
|
||||
|
@ -4,7 +4,7 @@ FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be02
|
||||
# Copy necessary files
|
||||
COPY scripts /scripts
|
||||
COPY pipeline /pipeline
|
||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||
COPY stirling-pdf/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||
#COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
|
@ -26,7 +26,7 @@ FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be02
|
||||
# Copy necessary files
|
||||
COPY scripts /scripts
|
||||
COPY pipeline /pipeline
|
||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||
COPY stirling-pdf/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||
COPY --from=build /app/build/libs/*.jar app.jar
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
@ -10,7 +10,7 @@ Fork Stirling-PDF and create a new branch out of `main`.
|
||||
|
||||
Then add a reference to the language in the navbar by adding a new language entry to the dropdown:
|
||||
|
||||
- Edit the file: [languages.html](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/templates/fragments/languages.html)
|
||||
- Edit the file: [languages.html](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/stirling-pdf/src/main/resources/templates/fragments/languages.html)
|
||||
|
||||
|
||||
For example, to add Polish, you would add:
|
||||
@ -25,7 +25,7 @@ The `data-bs-language-code` is the code used to reference the file in the next s
|
||||
|
||||
Start by copying the existing English property file:
|
||||
|
||||
- [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/messages_en_GB.properties)
|
||||
- [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/stirling-pdf/src/main/resources/messages_en_GB.properties)
|
||||
|
||||
Copy and rename it to `messages_{your data-bs-language-code here}.properties`. In the Polish example, you would set the name to `messages_pl_PL.properties`.
|
||||
|
||||
|
340
build.gradle
340
build.gradle
@ -26,21 +26,124 @@ ext {
|
||||
bouncycastleVersion = "1.80"
|
||||
springSecuritySamlVersion = "6.5.0"
|
||||
openSamlVersion = "4.3.2"
|
||||
commonmarkVersion = "0.24.0"
|
||||
tempJrePath = null
|
||||
}
|
||||
|
||||
group = "stirling.software"
|
||||
version = "0.46.2"
|
||||
|
||||
java {
|
||||
// 17 is lowest but we support and recommend 21
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
jar {
|
||||
enabled = false
|
||||
manifest {
|
||||
attributes "Implementation-Title": "Stirling-PDF",
|
||||
"Implementation-Version": project.version
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url = "https://build.shibboleth.net/maven/releases" }
|
||||
maven { url = "https://maven.pkg.github.com/jcefmaven/jcefmaven" }
|
||||
bootJar {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
if (System.getenv('DOCKER_ENABLE_SECURITY') == 'false' || System.getenv('DISABLE_ADDITIONAL_FEATURES') == 'true'
|
||||
|| (project.hasProperty('DISABLE_ADDITIONAL_FEATURES')
|
||||
&& System.getProperty('DISABLE_ADDITIONAL_FEATURES') == 'true')) {
|
||||
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' || System.getenv('DISABLE_ADDITIONAL_FEATURES') == 'true'
|
||||
|| (project.hasProperty('DISABLE_ADDITIONAL_FEATURES')
|
||||
&& System.getProperty('DISABLE_ADDITIONAL_FEATURES') == 'true')) {
|
||||
exclude 'stirling/software/proprietary/security/**'
|
||||
}
|
||||
|
||||
if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') {
|
||||
exclude 'stirling/software/SPDF/UI/impl/**'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group = 'stirling.software'
|
||||
version = '0.46.2'
|
||||
|
||||
configurations.configureEach {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'com.diffplug.spotless'
|
||||
apply plugin: 'org.springframework.boot'
|
||||
apply plugin: 'io.spring.dependency-management'
|
||||
|
||||
java {
|
||||
// 17 is lowest but we support and recommend 21
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
bootJar {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
configurations.configureEach {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
|
||||
// Exclude vulnerable BouncyCastle version used in tableau
|
||||
exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on'
|
||||
exclude group: 'org.bouncycastle', module: 'bcutil-jdk15on'
|
||||
exclude group: 'org.bouncycastle', module: 'bcmail-jdk15on'
|
||||
}
|
||||
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "org.springframework.boot:spring-boot-dependencies:$springBootVersion"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
implementation 'io.github.pixee:java-security-toolkit:1.2.1'
|
||||
|
||||
//tmp for security bumps
|
||||
implementation 'ch.qos.logback:logback-core:1.5.18'
|
||||
implementation 'ch.qos.logback:logback-classic:1.5.18'
|
||||
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = "UTF-8"
|
||||
dependsOn "spotlessApply"
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.compilerArgs << "-parameters"
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
|
||||
licenseReport {
|
||||
@ -99,10 +202,9 @@ jpackage {
|
||||
mainJar = "Stirling-PDF-${project.version}.jar"
|
||||
appName = "Stirling PDF"
|
||||
appVersion = project.version
|
||||
// appVersion = "2005.45.1"
|
||||
vendor = "Stirling PDF Inc"
|
||||
appDescription = "Stirling PDF - Your Local PDF Editor"
|
||||
icon = "src/main/resources/static/favicon.ico"
|
||||
icon = "stirling-pdf/src/main/resources/static/favicon.ico"
|
||||
verbose = true
|
||||
// mainClass = "org.springframework.boot.loader.launch.JarLauncher"
|
||||
|
||||
@ -141,10 +243,10 @@ jpackage {
|
||||
installDir = "C:/Program Files/Stirling-PDF"
|
||||
}
|
||||
|
||||
// macOS-specific configuration
|
||||
// MacOS-specific configuration
|
||||
mac {
|
||||
appVersion = getMacVersion(project.version.toString())
|
||||
icon = "src/main/resources/static/favicon.icns"
|
||||
icon = "stirling-pdf/src/main/resources/static/favicon.icns"
|
||||
type = "dmg"
|
||||
macPackageIdentifier = "Stirling PDF"
|
||||
macPackageName = "Stirling PDF"
|
||||
@ -166,7 +268,7 @@ jpackage {
|
||||
// Linux-specific configuration
|
||||
linux {
|
||||
appVersion = project.version
|
||||
icon = "src/main/resources/static/favicon.png"
|
||||
icon = "stirling-pdf/src/main/resources/static/favicon.png"
|
||||
type = "deb" // Can also use "rpm" for Red Hat-based systems
|
||||
|
||||
// Debian package configuration
|
||||
@ -202,10 +304,15 @@ jpackage {
|
||||
]*/
|
||||
|
||||
// Add copyright and license information
|
||||
copyright = "Copyright © 2024 Stirling Software"
|
||||
copyright = "Copyright © 2025 Stirling PDF Inc."
|
||||
licenseFile = "LICENSE"
|
||||
}
|
||||
|
||||
//tasks.wrapper {
|
||||
// gradleVersion = "8.14"
|
||||
// distributionType = Wrapper.DistributionType.ALL
|
||||
//}
|
||||
|
||||
tasks.register('jpackageMacX64') {
|
||||
group = 'distribution'
|
||||
description = 'Packages app for MacOS x86_64'
|
||||
@ -238,7 +345,7 @@ tasks.register('jpackageMacX64') {
|
||||
'--main-class', 'org.springframework.boot.loader.launch.JarLauncher',
|
||||
'--runtime-image', file(jrePath + "/zulu-17.jre/Contents/Home"),
|
||||
'--dest', 'build/jpackage/x86_64',
|
||||
'--icon', 'src/main/resources/static/favicon.icns',
|
||||
'--icon', 'stirling-pdf/src/main/resources/static/favicon.icns',
|
||||
'--app-version', getMacVersion(project.version.toString()),
|
||||
'--mac-package-name', 'Stirling PDF (x86_64)',
|
||||
'--mac-package-identifier', 'Stirling PDF (x86_64)',
|
||||
@ -287,12 +394,12 @@ tasks.register('downloadTempJre') {
|
||||
def jreArchive = new File(tmpDir, 'jre.tar.gz')
|
||||
def jreDir = new File(tmpDir, 'jre')
|
||||
|
||||
println "Downloading JRE to $jreArchive..."
|
||||
println "Downloading JRE to $jreArchive"
|
||||
jreArchive.withOutputStream { out ->
|
||||
new URI(jreUrl).toURL().withInputStream { from -> out << from }
|
||||
}
|
||||
|
||||
println "Extracting JRE to $jreDir..."
|
||||
println "Extracting JRE to $jreDir"
|
||||
jreDir.mkdirs()
|
||||
providers.exec {
|
||||
commandLine 'tar', '-xzf', jreArchive.absolutePath, '-C', jreDir.absolutePath, '--strip-components=1'
|
||||
@ -324,7 +431,7 @@ tasks.register('cleanTempJre') {
|
||||
}
|
||||
|
||||
launch4j {
|
||||
icon = "${projectDir}/src/main/resources/static/favicon.ico"
|
||||
icon = "${projectDir}/stirling-pdf/src/main/resources/static/favicon.ico"
|
||||
|
||||
outfile="Stirling-PDF.exe"
|
||||
|
||||
@ -335,7 +442,7 @@ launch4j {
|
||||
}
|
||||
jarTask = tasks.bootJar
|
||||
|
||||
errTitle="Encountered error, Do you have Java 21?"
|
||||
errTitle="Encountered error, do you have Java 21?"
|
||||
downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe"
|
||||
|
||||
if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') {
|
||||
@ -377,159 +484,12 @@ sonar {
|
||||
property "sonar.projectKey", "Stirling-Tools_Stirling-PDF"
|
||||
property "sonar.organization", "stirling-tools"
|
||||
|
||||
property "sonar.exclusions", "**/build-wrapper-dump.json, src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**"
|
||||
property "sonar.coverage.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**"
|
||||
property "sonar.cpd.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**"
|
||||
property "sonar.exclusions", "**/build-wrapper-dump.json, **/src/main/java/org/apache/**, **/src/main/resources/static/pdfjs/**, **/src/main/resources/static/pdfjs-legacy/**, **/src/main/resources/static/js/thirdParty/**"
|
||||
property "sonar.coverage.exclusions", "**/src/main/java/org/apache/**, **/src/main/resources/static/pdfjs/**, **/src/main/resources/static/pdfjs-legacy/**, **/src/main/resources/static/js/thirdParty/**"
|
||||
property "sonar.cpd.exclusions", "**/src/main/java/org/apache/**, **/src/main/resources/static/pdfjs/**, **/src/main/resources/static/pdfjs-legacy/**, **/src/main/resources/static/js/thirdParty/**"
|
||||
}
|
||||
}
|
||||
|
||||
//gradleLint {
|
||||
// rules=['unused-dependency']
|
||||
// }
|
||||
tasks.wrapper {
|
||||
gradleVersion = "8.14"
|
||||
distributionType = Wrapper.DistributionType.ALL
|
||||
}
|
||||
//tasks.withType(JavaCompile) {
|
||||
// options.compilerArgs << "-Xlint:deprecation"
|
||||
//}
|
||||
configurations.all {
|
||||
// Remove all commons-logging dependencies so that only spring-jcl is used
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
// Exclude Tomcat
|
||||
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':common')
|
||||
|
||||
//tmp for security bumps
|
||||
implementation 'ch.qos.logback:logback-core:1.5.18'
|
||||
implementation 'ch.qos.logback:logback-classic:1.5.18'
|
||||
|
||||
// Exclude vulnerable BouncyCastle version used in tableau
|
||||
configurations.all {
|
||||
exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on'
|
||||
exclude group: 'org.bouncycastle', module: 'bcutil-jdk15on'
|
||||
exclude group: 'org.bouncycastle', module: 'bcmail-jdk15on'
|
||||
}
|
||||
|
||||
if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") {
|
||||
implementation "me.friwi:jcefmaven:135.0.20"
|
||||
implementation "org.openjfx:javafx-controls:21"
|
||||
implementation "org.openjfx:javafx-swing:21"
|
||||
}
|
||||
|
||||
//security updates
|
||||
implementation "org.springframework:spring-webmvc:6.2.7"
|
||||
|
||||
implementation("io.github.pixee:java-security-toolkit:1.2.1")
|
||||
|
||||
// Exclude Tomcat and include Jetty
|
||||
// implementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
|
||||
implementation "org.springframework.boot:spring-boot-starter-jetty:$springBootVersion"
|
||||
// implementation "org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion"
|
||||
implementation 'com.posthog.java:posthog:1.2.0'
|
||||
implementation 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1'
|
||||
implementation 'org.snakeyaml:snakeyaml-engine:2.9'
|
||||
|
||||
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false" || System.getenv('DISABLE_ADDITIONAL_FEATURES') != 'true'
|
||||
|| (project.hasProperty('DISABLE_ADDITIONAL_FEATURES')
|
||||
&& System.getProperty('DISABLE_ADDITIONAL_FEATURES') != 'true')) {
|
||||
implementation project(':proprietary')
|
||||
}
|
||||
|
||||
// Batik
|
||||
implementation "org.apache.xmlgraphics:batik-all:1.19"
|
||||
|
||||
// TwelveMonkeys
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-bmp:$imageioVersion"
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-jpeg:$imageioVersion"
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-tiff:$imageioVersion"
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-webp:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-hdr:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-icns:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-iff:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pcx:$imageioVersion@
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pict:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pnm:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-psd:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-sgi:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-tga:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-thumbsdb:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-xwd:$imageioVersion"
|
||||
|
||||
// Image metadata extractor
|
||||
implementation "com.drewnoakes:metadata-extractor:2.19.0"
|
||||
implementation "commons-io:commons-io:2.19.0"
|
||||
// implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8"
|
||||
|
||||
// General PDF
|
||||
// https://mvnrepository.com/artifact/com.opencsv/opencsv
|
||||
implementation ("com.opencsv:opencsv:5.11")
|
||||
// implementation ("org.apache.pdfbox:pdfbox:$pdfboxVersion")
|
||||
implementation "org.apache.pdfbox:preflight:$pdfboxVersion"
|
||||
implementation ("org.apache.pdfbox:xmpbox:$pdfboxVersion")
|
||||
|
||||
// https://mvnrepository.com/artifact/technology.tabula/tabula
|
||||
implementation ('technology.tabula:tabula:1.0.5') {
|
||||
exclude group: "org.slf4j", module: "slf4j-simple"
|
||||
exclude group: "org.bouncycastle", module: "bcprov-jdk15on"
|
||||
exclude group: "com.google.code.gson", module: "gson"
|
||||
exclude group: "commons-io", module: "commons-io"
|
||||
}
|
||||
|
||||
implementation 'org.apache.pdfbox:jbig2-imageio:3.0.4'
|
||||
|
||||
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
|
||||
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
|
||||
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
||||
implementation "io.micrometer:micrometer-core:1.15.0"
|
||||
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
|
||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||
implementation "org.commonmark:commonmark:0.24.0"
|
||||
implementation "org.commonmark:commonmark-ext-gfm-tables:0.24.0"
|
||||
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
|
||||
implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0"
|
||||
implementation "com.fathzer:javaluator:3.0.6"
|
||||
implementation 'com.vladsch.flexmark:flexmark-html2md-converter:0.64.8'
|
||||
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion")
|
||||
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||
|
||||
// Mockito (core)
|
||||
testImplementation 'org.mockito:mockito-core:5.18.0'
|
||||
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
|
||||
testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = "UTF-8"
|
||||
dependsOn "spotlessApply"
|
||||
}
|
||||
compileJava {
|
||||
options.compilerArgs << "-parameters"
|
||||
}
|
||||
|
||||
task writeVersion {
|
||||
def propsFile = file("$projectDir/src/main/resources/version.properties")
|
||||
def propsDir = propsFile.parentFile
|
||||
|
||||
doLast {
|
||||
if (!propsDir.exists()) {
|
||||
propsDir.mkdirs()
|
||||
}
|
||||
|
||||
def props = new Properties()
|
||||
props.setProperty("version", version)
|
||||
props.store(propsFile.newWriter(), null)
|
||||
}
|
||||
}
|
||||
|
||||
processResources.dependsOn(writeVersion)
|
||||
|
||||
swaggerhubUpload {
|
||||
// dependsOn = generateOpenApiDocs // Depends on your task generating Swagger docs
|
||||
api = "Stirling-PDF" // The name of your API on SwaggerHub
|
||||
@ -540,25 +500,43 @@ swaggerhubUpload {
|
||||
oas = "3.0.0" // The version of the OpenAPI Specification you"re using
|
||||
}
|
||||
|
||||
jar {
|
||||
enabled = false
|
||||
manifest {
|
||||
attributes "Implementation-Title": "Stirling-PDF",
|
||||
"Implementation-Version": project.version
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("test") {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
task printVersion {
|
||||
tasks.register('writeVersion') {
|
||||
def propsFile = file("$projectDir/stirling-pdf/src/main/resources/version.properties")
|
||||
def propsDir = propsFile.parentFile
|
||||
|
||||
doLast {
|
||||
if (propsDir.exists()) {
|
||||
if (propsFile.exists()) {
|
||||
println "File exists: $propsFile"
|
||||
} else {
|
||||
println "$propsFile does not exist. Creating file."
|
||||
propsFile.createNewFile()
|
||||
}
|
||||
} else {
|
||||
println "Creating directory: $propsDir"
|
||||
propsDir.mkdirs()
|
||||
propsFile.createNewFile()
|
||||
}
|
||||
|
||||
def props = new Properties()
|
||||
props.setProperty("version", version)
|
||||
props.store(propsFile.newWriter(), null)
|
||||
}
|
||||
}
|
||||
|
||||
processResources.dependsOn(writeVersion)
|
||||
|
||||
tasks.register('printVersion') {
|
||||
doLast {
|
||||
println project.version
|
||||
}
|
||||
}
|
||||
|
||||
task printMacVersion {
|
||||
tasks.register('printMacVersion') {
|
||||
doLast {
|
||||
println getMacVersion(project.version.toString())
|
||||
}
|
||||
|
1
common/.gitignore
vendored
1
common/.gitignore
vendored
@ -124,6 +124,7 @@ SwaggerDoc.json
|
||||
*.rar
|
||||
*.db
|
||||
/build
|
||||
/common/build/
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
@ -1,41 +1,9 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'io.spring.dependency-management' version '1.1.7'
|
||||
}
|
||||
|
||||
group = 'stirling.software'
|
||||
version = '0.46.2'
|
||||
|
||||
ext {
|
||||
lombokVersion = "1.18.38"
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
|
||||
}
|
||||
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'org.springframework.boot:spring-boot-dependencies:3.5.0'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api 'org.springframework.boot:spring-boot-starter-web'
|
||||
api 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
api 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1'
|
||||
api 'com.fathzer:javaluator:3.0.6'
|
||||
api 'com.posthog.java:posthog:1.2.0'
|
||||
api 'io.github.pixee:java-security-toolkit:1.2.1'
|
||||
api 'org.apache.commons:commons-lang3:3.17.0'
|
||||
api 'com.drewnoakes:metadata-extractor:2.19.0' // Image metadata extractor
|
||||
api 'com.vladsch.flexmark:flexmark-html2md-converter:0.64.8'
|
||||
@ -43,10 +11,4 @@ dependencies {
|
||||
api 'jakarta.servlet:jakarta.servlet-api:6.1.0'
|
||||
api 'org.snakeyaml:snakeyaml-engine:2.9'
|
||||
api "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8"
|
||||
|
||||
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||
|
||||
testImplementation "org.springframework.boot:spring-boot-starter-test"
|
||||
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package stirling.software.common.configuration;
|
||||
|
||||
import io.github.pixee.security.SystemCommand;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -149,12 +151,12 @@ public class AppConfig {
|
||||
|
||||
@Bean(name = "activeSecurity")
|
||||
public boolean activeSecurity() {
|
||||
String additionalFeaturesOff = env.getProperty("DISABLE_ADDITIONAL_FEATURES");
|
||||
String disableAdditionalFeatures = env.getProperty("DISABLE_ADDITIONAL_FEATURES");
|
||||
|
||||
if (additionalFeaturesOff != null) {
|
||||
if (disableAdditionalFeatures != null) {
|
||||
// DISABLE_ADDITIONAL_FEATURES=true means security OFF, so return false
|
||||
// DISABLE_ADDITIONAL_FEATURES=false means security ON, so return true
|
||||
return !Boolean.parseBoolean(additionalFeaturesOff);
|
||||
return !Boolean.parseBoolean(disableAdditionalFeatures);
|
||||
}
|
||||
|
||||
return env.getProperty("DOCKER_ENABLE_SECURITY", Boolean.class, true);
|
||||
@ -164,7 +166,7 @@ public class AppConfig {
|
||||
@ConditionalOnMissingClass(
|
||||
"stirling.software.proprietary.security.configuration.SecurityConfiguration")
|
||||
public boolean missingActiveSecurity() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Bean(name = "directoryFilter")
|
||||
|
@ -344,10 +344,10 @@ public class ApplicationProperties {
|
||||
@Override
|
||||
public String toString() {
|
||||
return """
|
||||
Driver {
|
||||
driverName='%s'
|
||||
}
|
||||
"""
|
||||
Driver {
|
||||
driverName='%s'
|
||||
}
|
||||
"""
|
||||
.formatted(driverName);
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ public class PostHogService {
|
||||
|
||||
// New environment variables
|
||||
dockerMetrics.put("version_tag", System.getenv("VERSION_TAG"));
|
||||
dockerMetrics.put("without_enhanced_features", System.getenv("WITHOUT_ENHANCED_FEATURES"));
|
||||
dockerMetrics.put("additional_features_off", System.getenv("ADDITIONAL_FEATURES_OFF"));
|
||||
dockerMetrics.put("fat_docker", System.getenv("FAT_DOCKER"));
|
||||
|
||||
return dockerMetrics;
|
||||
|
@ -0,0 +1,14 @@
|
||||
package stirling.software.common.util;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class ValidationUtil {
|
||||
|
||||
public static boolean isStringEmpty(String input) {
|
||||
return input == null || input.isBlank();
|
||||
}
|
||||
|
||||
public static boolean isCollectionEmpty(Collection<String> input) {
|
||||
return input == null || input.isEmpty();
|
||||
}
|
||||
}
|
@ -1,5 +1,14 @@
|
||||
package stirling.software.common.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
@ -10,18 +19,6 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
||||
|
||||
class CheckProgramInstallTest {
|
||||
|
||||
private MockedStatic<ProcessExecutor> mockProcessExecutor;
|
||||
|
@ -19,7 +19,6 @@ import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import stirling.software.common.configuration.RuntimePathConfig;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
|
@ -1,10 +1,7 @@
|
||||
package stirling.software.common.util;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@ -12,11 +9,13 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import stirling.software.common.model.enumeration.UsernameAttribute;
|
||||
import stirling.software.common.model.oauth2.GitHubProvider;
|
||||
import stirling.software.common.model.oauth2.GoogleProvider;
|
||||
import stirling.software.common.model.oauth2.Provider;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ProviderUtilsTest {
|
||||
@ -29,19 +28,19 @@ class ProviderUtilsTest {
|
||||
when(provider.getClientSecret()).thenReturn("clientSecret");
|
||||
when(provider.getScopes()).thenReturn(List.of("read:user"));
|
||||
|
||||
Assertions.assertTrue(ProviderUtils.validateProvider(provider));
|
||||
assertTrue(ProviderUtils.validateProvider(provider));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("providerParams")
|
||||
void testUnsuccessfulValidation(Provider provider) {
|
||||
Assertions.assertFalse(ProviderUtils.validateProvider(provider));
|
||||
assertFalse(ProviderUtils.validateProvider(provider));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> providerParams() {
|
||||
Provider generic = null;
|
||||
var google =
|
||||
new GoogleProvider(null, "clientSecret", List.of("scope"), UsernameAttribute.EMAIL);
|
||||
new GoogleProvider(null, "clientSecret", List.of("scope"), UsernameAttribute.EMAIL);
|
||||
var github = new GitHubProvider("clientId", "", List.of("scope"), UsernameAttribute.LOGIN);
|
||||
|
||||
return Stream.of(Arguments.of(generic), Arguments.of(google), Arguments.of(github));
|
||||
|
@ -1,12 +1,10 @@
|
||||
package stirling.software.common.util.misc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import stirling.software.common.model.api.misc.HighContrastColorCombination;
|
||||
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class HighContrastColorReplaceDeciderTest {
|
||||
|
||||
|
@ -26,7 +26,6 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
||||
|
||||
class InvertFullColorStrategyTest {
|
||||
|
@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
||||
|
||||
class ReplaceAndInvertColorStrategyTest {
|
||||
|
@ -1,17 +1,14 @@
|
||||
package stirling.software.common.util.propertyeditor;
|
||||
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import stirling.software.common.model.api.security.RedactionArea;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import stirling.software.common.model.api.security.RedactionArea;
|
||||
|
||||
class StringToArrayListPropertyEditorTest {
|
||||
|
||||
private StringToArrayListPropertyEditor editor;
|
||||
|
@ -14,9 +14,9 @@ services:
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /stirling/latest/data:/usr/share/tessdata:rw
|
||||
- /stirling/latest/config:/configs:rw
|
||||
- /stirling/latest/logs:/logs:rw
|
||||
- ./stirling/latest/data:/usr/share/tessdata:rw
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "false"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
|
@ -14,9 +14,9 @@ services:
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /stirling/latest/data:/usr/share/tessdata:rw
|
||||
- /stirling/latest/config:/configs:rw
|
||||
- /stirling/latest/logs:/logs:rw
|
||||
- ./stirling/latest/data:/usr/share/tessdata:rw
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "false"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
|
@ -14,8 +14,8 @@ services:
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /stirling/latest/config:/configs:rw
|
||||
- /stirling/latest/logs:/logs:rw
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "true"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
|
@ -14,9 +14,9 @@ services:
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /stirling/latest/data:/usr/share/tessdata:rw
|
||||
- /stirling/latest/config:/configs:rw
|
||||
- /stirling/latest/logs:/logs:rw
|
||||
- ./stirling/latest/data:/usr/share/tessdata:rw
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "true"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
|
@ -14,9 +14,9 @@ services:
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- /stirling/latest/data:/usr/share/tessdata:rw
|
||||
- /stirling/latest/config:/configs:rw
|
||||
- /stirling/latest/logs:/logs:rw
|
||||
- ./stirling/latest/data:/usr/share/tessdata:rw
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "false"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
|
1
proprietary/.gitignore
vendored
1
proprietary/.gitignore
vendored
@ -124,6 +124,7 @@ SwaggerDoc.json
|
||||
*.rar
|
||||
*.db
|
||||
/build
|
||||
/proprietary/build/
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
@ -1,28 +1,7 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'io.spring.dependency-management' version '1.1.7'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url = "https://build.shibboleth.net/maven/releases" }
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
|
||||
}
|
||||
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'org.springframework.boot:spring-boot-dependencies:3.5.0'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':common')
|
||||
|
||||
@ -42,7 +21,6 @@ dependencies {
|
||||
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
|
||||
implementation 'org.bouncycastle:bcprov-jdk18on:1.80'
|
||||
|
||||
implementation 'io.github.pixee:java-security-toolkit:1.2.1'
|
||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE'
|
||||
api 'io.micrometer:micrometer-registry-prometheus'
|
||||
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
|
||||
@ -54,14 +32,6 @@ dependencies {
|
||||
implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion"
|
||||
}
|
||||
implementation 'com.coveo:saml-client:5.0.0'
|
||||
|
||||
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.10.0')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
|
||||
}
|
||||
|
||||
tasks.register('prepareKotlinBuildScriptModel') {}
|
||||
|
@ -38,6 +38,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||
public static final String LOGOUT_PATH = "/login?logout=true";
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
private final AppConfig appConfig;
|
||||
|
||||
@Override
|
||||
|
@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
@ -55,6 +56,7 @@ import stirling.software.proprietary.security.session.SessionPersistentRegistry;
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity
|
||||
@DependsOn("runningProOrHigher")
|
||||
public class SecurityConfiguration {
|
||||
|
||||
private final CustomUserDetailsService userDetailsService;
|
||||
|
@ -1,13 +1,13 @@
|
||||
package stirling.software.SPDF.EE;
|
||||
package stirling.software.proprietary.security.configuration.ee;
|
||||
|
||||
import static stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.model.ApplicationProperties.EnterpriseEdition;
|
||||
import stirling.software.common.model.ApplicationProperties.Premium;
|
||||
@ -15,7 +15,6 @@ import stirling.software.common.model.ApplicationProperties.Premium.ProFeatures.
|
||||
|
||||
@Configuration
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@Slf4j
|
||||
public class EEAppConfig {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
@ -30,6 +29,7 @@ public class EEAppConfig {
|
||||
}
|
||||
|
||||
@Bean(name = "runningProOrHigher")
|
||||
@Qualifier("runningProOrHigher")
|
||||
public boolean runningProOrHigher() {
|
||||
return licenseKeyChecker.getPremiumLicenseEnabledResult() != License.NORMAL;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.SPDF.EE;
|
||||
package stirling.software.proprietary.security.configuration.ee;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
@ -27,7 +27,7 @@ import stirling.software.common.util.GeneralUtils;
|
||||
@RequiredArgsConstructor
|
||||
public class KeygenLicenseVerifier {
|
||||
|
||||
enum License {
|
||||
public enum License {
|
||||
NORMAL,
|
||||
PRO,
|
||||
ENTERPRISE
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.SPDF.EE;
|
||||
package stirling.software.proprietary.security.configuration.ee;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@ -10,12 +10,12 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@Component
|
||||
public class LicenseKeyChecker {
|
||||
|
||||
private static final String FILE_PREFIX = "file:";
|
@ -5,7 +5,6 @@ import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.opensaml.saml.saml2.core.AuthnRequest;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -30,8 +29,8 @@ import stirling.software.common.model.ApplicationProperties.Security.SAML2;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
@ConditionalOnProperty(value = "security.saml2.enabled", havingValue = "true")
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnBooleanProperty("security.saml2.enabled")
|
||||
public class SAML2Configuration {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
@ -1,7 +1,10 @@
|
||||
package stirling.software.SPDF.EE;
|
||||
package stirling.software.proprietary.security.configuration.ee;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@ -13,7 +16,6 @@ import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
@ -1,5 +1,5 @@
|
||||
echo "Running Stirling PDF with DISABLE_ADDITIONAL_FEATURES=${DISABLE_ADDITIONAL_FEATURES} and VERSION_TAG=${VERSION_TAG}"
|
||||
# Check for $DISABLE_ADDITIONAL_FEATURES and download the appropriate JAR if required
|
||||
# Check for DISABLE_ADDITIONAL_FEATURES and download the appropriate JAR if required
|
||||
if [ "$DISABLE_ADDITIONAL_FEATURES" = "false" ] && [ "$VERSION_TAG" != "alpha" ]; then
|
||||
if [ ! -f app-security.jar ]; then
|
||||
echo "Trying to download from: https://files.stirlingpdf.com/v$VERSION_TAG/Stirling-PDF-with-login.jar"
|
||||
|
@ -4,4 +4,4 @@ plugins {
|
||||
}
|
||||
rootProject.name = 'Stirling-PDF'
|
||||
|
||||
include 'common', 'proprietary'
|
||||
include 'stirling-pdf', 'common', 'proprietary'
|
||||
|
@ -1,77 +0,0 @@
|
||||
// package stirling.software.SPDF.config.fingerprint;
|
||||
//
|
||||
// import java.security.MessageDigest;
|
||||
// import java.security.NoSuchAlgorithmException;
|
||||
//
|
||||
// import org.springframework.stereotype.Component;
|
||||
//
|
||||
// import jakarta.servlet.http.HttpServletRequest;
|
||||
//
|
||||
// @Component
|
||||
// public class FingerprintGenerator {
|
||||
//
|
||||
// public String generateFingerprint(HttpServletRequest request) {
|
||||
// if (request == null) {
|
||||
// return "";
|
||||
// }
|
||||
// StringBuilder fingerprintBuilder = new StringBuilder();
|
||||
//
|
||||
// // Add IP address
|
||||
// fingerprintBuilder.append(request.getRemoteAddr());
|
||||
//
|
||||
// // Add X-Forwarded-For header if present (for clients behind proxies)
|
||||
// String forwardedFor = request.getHeader("X-Forwarded-For");
|
||||
// if (forwardedFor != null) {
|
||||
// fingerprintBuilder.append(forwardedFor);
|
||||
// }
|
||||
//
|
||||
// // Add User-Agent
|
||||
// String userAgent = request.getHeader("User-Agent");
|
||||
// if (userAgent != null) {
|
||||
// fingerprintBuilder.append(userAgent);
|
||||
// }
|
||||
//
|
||||
// // Add Accept-Language header
|
||||
// String acceptLanguage = request.getHeader("Accept-Language");
|
||||
// if (acceptLanguage != null) {
|
||||
// fingerprintBuilder.append(acceptLanguage);
|
||||
// }
|
||||
//
|
||||
// // Add Accept header
|
||||
// String accept = request.getHeader("Accept");
|
||||
// if (accept != null) {
|
||||
// fingerprintBuilder.append(accept);
|
||||
// }
|
||||
//
|
||||
// // Add Connection header
|
||||
// String connection = request.getHeader("Connection");
|
||||
// if (connection != null) {
|
||||
// fingerprintBuilder.append(connection);
|
||||
// }
|
||||
//
|
||||
// // Add server port
|
||||
// fingerprintBuilder.append(request.getServerPort());
|
||||
//
|
||||
// // Add secure flag
|
||||
// fingerprintBuilder.append(request.isSecure());
|
||||
//
|
||||
// // Generate a hash of the fingerprint
|
||||
// return generateHash(fingerprintBuilder.toString());
|
||||
// }
|
||||
//
|
||||
// private String generateHash(String input) {
|
||||
// try {
|
||||
// MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
// byte[] hash = digest.digest(input.getBytes());
|
||||
// StringBuilder hexString = new StringBuilder();
|
||||
// for (byte b : hash) {
|
||||
// String hex = Integer.toHexString(0xff & b);
|
||||
// if (hex.length() == 1) hexString.append('0');
|
||||
// hexString.append(hex);
|
||||
// }
|
||||
// return hexString.toString();
|
||||
// } catch (NoSuchAlgorithmException e) {
|
||||
// throw new RuntimeException("Failed to generate fingerprint hash", e);
|
||||
// }
|
||||
// }
|
||||
// }
|
@ -1,38 +0,0 @@
|
||||
package stirling.software.SPDF.model.api;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ConditionalOnProperty(value = "mail.enabled", havingValue = "true", matchIfMissing = false)
|
||||
public class Email extends GeneralFile {
|
||||
|
||||
@Schema(
|
||||
description = "The recipient's email address",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
format = "email")
|
||||
private String to;
|
||||
|
||||
@Schema(
|
||||
description = "The subject of the email",
|
||||
defaultValue = "Stirling Software PDF Notification",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private String subject;
|
||||
|
||||
@Schema(
|
||||
description = "The body of the email",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
|
||||
defaultValue =
|
||||
"This message was automatically generated by Stirling-PDF, an innovative"
|
||||
+ " solution from Stirling Software. For more information, visit our <a"
|
||||
+ " href=\"https://stirling-software.com\">website</a>.<br><br>Please do"
|
||||
+ " not reply directly to this email.")
|
||||
private String body;
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package stirling.software.SPDF.model.api;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
public class GeneralFile {
|
||||
|
||||
@Schema(
|
||||
description = "The input file",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
format = "binary")
|
||||
private MultipartFile fileInput;
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package stirling.software.SPDF.model.api.converters;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import stirling.software.SPDF.model.api.PDFWithPageNums;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ConvertToImageRequest extends PDFWithPageNums {
|
||||
|
||||
@Schema(
|
||||
description = "The output image format",
|
||||
defaultValue = "png",
|
||||
allowableValues = {"png", "jpeg", "jpg", "gif", "webp"},
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String imageFormat;
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"Choose between a single image containing all pages or separate images for each"
|
||||
+ " page",
|
||||
defaultValue = "multiple",
|
||||
allowableValues = {"single", "multiple"},
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String singleOrMultiple;
|
||||
|
||||
@Schema(
|
||||
description = "The color type of the output image(s)",
|
||||
defaultValue = "color",
|
||||
allowableValues = {"color", "greyscale", "blackwhite"},
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String colorType;
|
||||
|
||||
@Schema(
|
||||
description = "The DPI (dots per inch) for the output image(s)",
|
||||
defaultValue = "300",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer dpi;
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package stirling.software.SPDF.model.api.general;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
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 OverlayPdfsRequest extends PDFFile {
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"An array of PDF files to be used as overlays on the base PDF. The order in"
|
||||
+ " these files is applied based on the selected mode.",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private MultipartFile[] overlayFiles;
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"The mode of overlaying: 'SequentialOverlay' for sequential application,"
|
||||
+ " 'InterleavedOverlay' for round-robin application, 'FixedRepeatOverlay'"
|
||||
+ " for fixed repetition based on provided counts",
|
||||
allowableValues = {"SequentialOverlay", "InterleavedOverlay", "FixedRepeatOverlay"},
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String overlayMode;
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"An array of integers specifying the number of times each corresponding overlay"
|
||||
+ " file should be applied in the 'FixedRepeatOverlay' mode. This should"
|
||||
+ " match the length of the overlayFiles array.",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private int[] counts;
|
||||
|
||||
@Schema(
|
||||
description = "Overlay position 0 is Foregound, 1 is Background",
|
||||
allowableValues = {"0", "1"},
|
||||
requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
type = "number")
|
||||
private int overlayPosition;
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
package stirling.software.SPDF.model.api.misc;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import stirling.software.SPDF.model.api.PDFWithPageNums;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AddStampRequest extends PDFWithPageNums {
|
||||
|
||||
@Schema(
|
||||
description = "The stamp type (text or image)",
|
||||
allowableValues = {"text", "image"},
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String stampType;
|
||||
|
||||
@Schema(description = "The stamp text", defaultValue = "Stirling Software")
|
||||
private String stampText;
|
||||
|
||||
@Schema(description = "The stamp image")
|
||||
private MultipartFile stampImage;
|
||||
|
||||
@Schema(
|
||||
description = "The selected alphabet of the stamp text",
|
||||
allowableValues = {"roman", "arabic", "japanese", "korean", "chinese"},
|
||||
defaultValue = "roman")
|
||||
private String alphabet = "roman";
|
||||
|
||||
@Schema(
|
||||
description = "The font size of the stamp text and image",
|
||||
defaultValue = "30",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private float fontSize;
|
||||
|
||||
@Schema(
|
||||
description = "The rotation of the stamp in degrees",
|
||||
defaultValue = "0",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private float rotation;
|
||||
|
||||
@Schema(
|
||||
description = "The opacity of the stamp (0.0 - 1.0)",
|
||||
defaultValue = "0.5",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private float opacity;
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"Position for stamp placement based on a 1-9 grid (1: bottom-left, 2: bottom-center,"
|
||||
+ " 3: bottom-right, 4: middle-left, 5: middle-center, 6: middle-right,"
|
||||
+ " 7: top-left, 8: top-center, 9: top-right)",
|
||||
allowableValues = {"1", "2", "3", "4", "5", "6", "7", "8", "9"},
|
||||
defaultValue = "5",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private int position;
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"Override X coordinate for stamp placement. If set, it will override the"
|
||||
+ " position-based calculation. Negative value means no override.",
|
||||
defaultValue = "-1",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private float overrideX; // Default to -1 indicating no override
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"Override Y coordinate for stamp placement. If set, it will override the"
|
||||
+ " position-based calculation. Negative value means no override.",
|
||||
defaultValue = "-1",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private float overrideY; // Default to -1 indicating no override
|
||||
|
||||
@Schema(
|
||||
description = "Specifies the margin size for the stamp.",
|
||||
allowableValues = {"small", "medium", "large", "x-large"},
|
||||
defaultValue = "medium",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String customMargin;
|
||||
|
||||
@Schema(description = "The color of the stamp text", defaultValue = "#d3d3d3")
|
||||
private String customColor;
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
package stirling.software.SPDF.model.api.misc;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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 MetadataRequest extends PDFFile {
|
||||
|
||||
@Schema(
|
||||
description = "Delete all metadata if set to true",
|
||||
defaultValue = "false",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean deleteAll;
|
||||
|
||||
@Schema(
|
||||
description = "The author of the document",
|
||||
defaultValue = "author",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private String author;
|
||||
|
||||
@Schema(
|
||||
description = "The creation date of the document (format: yyyy/MM/dd HH:mm:ss)",
|
||||
pattern = "yyyy/MM/dd HH:mm:ss",
|
||||
defaultValue = "2023/10/01 12:00:00",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private String creationDate;
|
||||
|
||||
@Schema(
|
||||
description = "The creator of the document",
|
||||
defaultValue = "creator",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private String creator;
|
||||
|
||||
@Schema(
|
||||
description = "The keywords for the document",
|
||||
defaultValue = "keywords",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private String keywords;
|
||||
|
||||
@Schema(
|
||||
description = "The modification date of the document (format: yyyy/MM/dd HH:mm:ss)",
|
||||
pattern = "yyyy/MM/dd HH:mm:ss",
|
||||
defaultValue = "2023/10/01 12:00:00",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private String modificationDate;
|
||||
|
||||
@Schema(
|
||||
description = "The producer of the document",
|
||||
defaultValue = "producer",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private String producer;
|
||||
|
||||
@Schema(
|
||||
description = "The subject of the document",
|
||||
defaultValue = "subject",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private String subject;
|
||||
|
||||
@Schema(
|
||||
description = "The title of the document",
|
||||
defaultValue = "title",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private String title;
|
||||
|
||||
@Schema(
|
||||
description = "The trapped status of the document",
|
||||
defaultValue = "False",
|
||||
allowableValues = {"True", "False", "Unknown"},
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
private String trapped;
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"Map list of key and value of custom parameters. Note these must start with"
|
||||
+ " customKey and customValue if they are non-standard")
|
||||
private Map<String, String> allRequestParams;
|
||||
}
|
@ -1,49 +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",
|
||||
defaultValue = "text,text2",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String listOfText;
|
||||
|
||||
@Schema(
|
||||
description = "Whether to use regex for the listOfText",
|
||||
defaultValue = "false",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean useRegex;
|
||||
|
||||
@Schema(
|
||||
description = "Whether to use whole word search",
|
||||
defaultValue = "false",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean wholeWordSearch;
|
||||
|
||||
@Schema(
|
||||
description = "The color for redaction",
|
||||
defaultValue = "#000000",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String redactColor;
|
||||
|
||||
@Schema(
|
||||
description = "Custom padding for redaction",
|
||||
type = "number",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private float customPadding;
|
||||
|
||||
@Schema(
|
||||
description = "Convert the redacted PDF to an image",
|
||||
defaultValue = "false",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean convertPDFToImage;
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package stirling.software.SPDF.model.api.security;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
public class RedactionArea {
|
||||
@Schema(description = "The left edge point of the area to be redacted.")
|
||||
private Double x;
|
||||
|
||||
@Schema(description = "The top edge point of the area to be redacted.")
|
||||
private Double y;
|
||||
|
||||
@Schema(description = "The height of the area to be redacted.")
|
||||
private Double height;
|
||||
|
||||
@Schema(description = "The width of the area to be redacted.")
|
||||
private Double width;
|
||||
|
||||
@Schema(description = "The page on which the area should be redacted.")
|
||||
private Integer page;
|
||||
|
||||
@Schema(description = "The color used to redact the specified area.")
|
||||
private String color;
|
||||
}
|
@ -1,49 +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 SanitizePdfRequest extends PDFFile {
|
||||
|
||||
@Schema(
|
||||
description = "Remove JavaScript actions from the PDF",
|
||||
defaultValue = "true",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean removeJavaScript;
|
||||
|
||||
@Schema(
|
||||
description = "Remove embedded files from the PDF",
|
||||
defaultValue = "true",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean removeEmbeddedFiles;
|
||||
|
||||
@Schema(
|
||||
description = "Remove XMP metadata from the PDF",
|
||||
defaultValue = "false",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean removeXMPMetadata;
|
||||
|
||||
@Schema(
|
||||
description = "Remove document info metadata from the PDF",
|
||||
defaultValue = "false",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean removeMetadata;
|
||||
|
||||
@Schema(
|
||||
description = "Remove links from the PDF",
|
||||
defaultValue = "false",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean removeLinks;
|
||||
|
||||
@Schema(
|
||||
description = "Remove fonts from the PDF",
|
||||
defaultValue = "false",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean removeFonts;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package stirling.software.SPDF.model.api.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UpdateUserDetails extends UpdateUserUsername {
|
||||
|
||||
@Schema(
|
||||
description = "new password for user",
|
||||
format = "password",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String newPassword;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package stirling.software.SPDF.model.api.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UpdateUserUsername extends UsernameAndPass {
|
||||
|
||||
@Schema(description = "new username for user")
|
||||
private String newUsername;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package stirling.software.SPDF.model.api.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
public class Username {
|
||||
|
||||
@Schema(description = "username of user", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String username;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package stirling.software.SPDF.model.api.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UsernameAndPass extends Username {
|
||||
|
||||
@Schema(description = "password of user", format = "password")
|
||||
private String password;
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,235 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
|
||||
xmlns:th="https://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title='')}"></th:block>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<div style="transform-origin: top;"
|
||||
id="scale-wrap">
|
||||
<br class="d-md-none">
|
||||
<!-- Features -->
|
||||
<script th:src="@{'/js/homecard.js'}"></script>
|
||||
<div style="
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;"
|
||||
>
|
||||
<div>
|
||||
<br>
|
||||
<div style="justify-content: center; display: flex;">
|
||||
<div style="margin:0 3rem">
|
||||
<div>
|
||||
<div
|
||||
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">
|
||||
<p class="lead fs-4"
|
||||
th:text="${@homeText != 'null' and @homeText != null and @homeText != ''} ? ${@homeText} : #{home.desc}">
|
||||
</p>
|
||||
</div>
|
||||
<div id="groupRecent" style="width: fit-content; margin: 0 auto">
|
||||
<div
|
||||
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.recent})}">
|
||||
</div>
|
||||
<div class="recent-features">
|
||||
<div class="newfeature"
|
||||
th:insert="~{fragments/navbarEntryCustom :: navbarEntry('redact', '/images/redact-manual.svg#icon-redact-manual', 'home.redact.title', 'home.redact.desc', 'redact.tags', 'security')}">
|
||||
</div>
|
||||
<div class="newfeature"
|
||||
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>
|
||||
<span class="material-symbols-rounded search-icon">
|
||||
search
|
||||
</span>
|
||||
<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="height:2.5rem; display: flex; align-items: center; cursor: pointer; justify-content: center;">
|
||||
<label for="sort-options" th:text="#{home.sortBy}">Sort by:</label>
|
||||
<select id="sort-options" style="border:none;">
|
||||
<option value="alphabetical" th:text="#{home.alphabetical}"> </option>
|
||||
<!-- <option value="personal">Your most used</option> -->
|
||||
<option value="global" th:text="#{home.globalPopularity}"></option>
|
||||
<!-- <option value="server">Popularity in organisation</option> -->
|
||||
</select>
|
||||
</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;">
|
||||
<div th:title="#{home.setFavorites}" style="display: flex; align-items: center; cursor: pointer;"
|
||||
onclick="toggleFavoritesMode()">
|
||||
<span class="material-symbols-rounded toggle-favourites"
|
||||
style="font-size: 2rem; margin-left: 0.2rem;">
|
||||
star
|
||||
</span>
|
||||
</div>
|
||||
<div onclick="toggleFavoritesView()" th:title="#{home.hideFavorites}" id="favouritesVisibility"
|
||||
style="display: flex; align-items: center; cursor: pointer;">
|
||||
<span id="toggle-favourites-icon" class="material-symbols-rounded toggle-favourites"
|
||||
style="font-size: 2rem; margin-left: 0.2rem;">
|
||||
visibility
|
||||
</span>
|
||||
</div>
|
||||
<a th:if="${@shouldShow}" href="https://github.com/Stirling-Tools/Stirling-PDF/releases"
|
||||
target="_blank" id="update-link" rel="noopener" th:title="#{settings.update}"
|
||||
style="text-decoration: none; color: inherit; cursor: pointer; display: flex; align-items: center;">
|
||||
<span class="material-symbols-rounded toggle-favourites"
|
||||
style="font-size: 2rem; margin-left: 0.2rem;">
|
||||
update
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
</div>
|
||||
<div class="features-container" style=" border-top: 1px;
|
||||
border-top-style: solid;
|
||||
border-color: var(--md-nav-color-on-seperator);
|
||||
margin-top: 1rem;
|
||||
">
|
||||
<div class="feature-rows">
|
||||
<div id="groupFavorites" class="feature-group">
|
||||
<div th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.favorite})}">
|
||||
</div>
|
||||
<div class="nav-group-container">
|
||||
</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>
|
||||
</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/crsr-tz6-487" 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>
|
||||
|
||||
|
||||
<!-- Analytics Modal -->
|
||||
<div class="modal fade" id="analyticsModal" tabindex="-1" role="dialog" aria-labelledby="analyticsModalLabel"
|
||||
aria-hidden="true" th:if="${@analyticsPrompt}">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<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>
|
||||
|
||||
|
||||
<style>
|
||||
.favorite-icon {
|
||||
cursor: pointer;
|
||||
width: 0rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.toggle-favourites {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggle-favourites.active {
|
||||
color: gold;
|
||||
}
|
||||
</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>
|
||||
|
||||
</html>
|
@ -1,163 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{fakeScan.title}, header=#{fakeScan.header})}"></th:block>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon advance">scanner</span>
|
||||
<span class="tool-header-text" th:text="#{fakeScan.title}"></span>
|
||||
</div>
|
||||
|
||||
<form method="post" enctype="multipart/form-data" id="uploadForm" th:action="@{'/api/v1/misc/fake-scan'}">
|
||||
<input type="hidden" name="advancedEnabled" id="advancedEnabled" value="false">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
|
||||
<br>
|
||||
<div class="mb-3">
|
||||
<label for="quality" class="form-label" th:text="#{fakeScan.quality}"></label>
|
||||
<select class="form-select" id="quality" name="quality">
|
||||
<option value="low" th:text="#{fakeScan.quality.low}"></option>
|
||||
<option value="medium" th:text="#{fakeScan.quality.medium}"></option>
|
||||
<option value="high" th:text="#{fakeScan.quality.high}" selected></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="rotation" class="form-label" th:text="#{fakeScan.rotation}"></label>
|
||||
<select class="form-select" id="rotation" name="rotation">
|
||||
<option value="none" th:text="#{fakeScan.rotation.none}"></option>
|
||||
<option value="slight" th:text="#{fakeScan.rotation.slight}" selected></option>
|
||||
<option value="moderate" th:text="#{fakeScan.rotation.moderate}"></option>
|
||||
<option value="severe" th:text="#{fakeScan.rotation.severe}"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="advancedSettingsToggle">
|
||||
<label class="form-check-label" for="advancedSettingsToggle" th:text="#{fakeScan.advancedSettings}"></label>
|
||||
</div>
|
||||
<div id="advancedSettings" style="display:none; border:1px solid #eee; padding:1em; border-radius:8px; margin-bottom:1em;">
|
||||
<div class="mb-3">
|
||||
<label for="colorspace" class="form-label" th:text="#{fakeScan.colorspace}"></label>
|
||||
<select class="form-select" id="colorspace" name="colorspace">
|
||||
<option value="grayscale" th:text="#{fakeScan.colorspace.grayscale}" selected></option>
|
||||
<option value="color" th:text="#{fakeScan.colorspace.color}"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="border" class="form-label" th:text="#{fakeScan.border}"></label>
|
||||
<input type="number" class="form-control" id="border" name="border" min="0" max="100" value="20">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="rotate" class="form-label" th:text="#{fakeScan.rotate}"></label>
|
||||
<input type="number" class="form-control" id="rotate" name="rotate" min="-15" max="15" value="0">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="rotateVariance" class="form-label" th:text="#{fakeScan.rotateVariance}"></label>
|
||||
<input type="number" class="form-control" id="rotateVariance" name="rotateVariance" min="0" max="10" value="2">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="brightness" class="form-label" th:text="#{fakeScan.brightness}"></label>
|
||||
<input type="range" class="form-range" id="brightness" name="brightness" min="0.5" max="1.5" step="0.01" value="1.0">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="contrast" class="form-label" th:text="#{fakeScan.contrast}"></label>
|
||||
<input type="range" class="form-range" id="contrast" name="contrast" min="0.5" max="1.5" step="0.01" value="1.0">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="blur" class="form-label" th:text="#{fakeScan.blur}"></label>
|
||||
<input type="range" class="form-range" id="blur" name="blur" min="0" max="5" step="0.1" value="1.0">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="noise" class="form-label" th:text="#{fakeScan.noise}"></label>
|
||||
<input type="range" class="form-range" id="noise" name="noise" min="0" max="20" step="0.1" value="8.0">
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="yellowish" name="yellowish">
|
||||
<label class="form-check-label" for="yellowish" th:text="#{fakeScan.yellowish}"></label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="resolution" class="form-label" th:text="#{fakeScan.resolution}"></label>
|
||||
<input type="number" class="form-control" id="resolution" name="resolution" min="72" max="600" value="300">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 text-left">
|
||||
<button type="submit" class="btn btn-primary" th:text="#{fakeScan.submit}" id="submitBtn"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
<script th:src="@{'/js/fetch-utils.js'}"></script>
|
||||
<script th:inline="javascript">
|
||||
// Show/hide advanced settings
|
||||
document.getElementById('advancedSettingsToggle').addEventListener('change', function() {
|
||||
document.getElementById('advancedSettings').style.display = this.checked ? 'block' : 'none';
|
||||
});
|
||||
|
||||
// Form submission handling
|
||||
const form = document.getElementById('uploadForm');
|
||||
if (form) {
|
||||
form.addEventListener('submit', function(e) {
|
||||
// If advanced settings are not enabled, remove advanced fields
|
||||
if (!document.getElementById('advancedSettingsToggle').checked) {
|
||||
const formData = new FormData(form);
|
||||
formData.delete('colorspace');
|
||||
formData.delete('border');
|
||||
formData.delete('rotate');
|
||||
formData.delete('rotateVariance');
|
||||
formData.delete('brightness');
|
||||
formData.delete('contrast');
|
||||
formData.delete('blur');
|
||||
formData.delete('noise');
|
||||
formData.delete('yellowish');
|
||||
formData.delete('resolution');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize advanced settings state
|
||||
const advancedSettingsToggle = document.getElementById('advancedSettingsToggle');
|
||||
const advancedSettings = document.getElementById('advancedSettings');
|
||||
if (advancedSettingsToggle && advancedSettings) {
|
||||
// Helper: enable/disable all fields in advanced settings
|
||||
function setAdvancedFieldsEnabled(enabled) {
|
||||
const fields = advancedSettings.querySelectorAll('input, select');
|
||||
fields.forEach(field => {
|
||||
field.disabled = !enabled;
|
||||
// If enabling and value is empty, set to default
|
||||
if (enabled && (field.value === '' || field.value == null)) {
|
||||
if (field.type === 'number' || field.type === 'range') {
|
||||
field.value = field.defaultValue;
|
||||
} else if (field.type === 'checkbox') {
|
||||
field.checked = field.defaultChecked;
|
||||
} else if (field.tagName === 'SELECT') {
|
||||
field.value = field.querySelector('option[selected]')?.value || field.options[0].value;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Set initial state
|
||||
setAdvancedFieldsEnabled(advancedSettingsToggle.checked);
|
||||
advancedSettings.style.display = advancedSettingsToggle.checked ? 'block' : 'none';
|
||||
document.getElementById('advancedEnabled').value = advancedSettingsToggle.checked ? 'true' : 'false';
|
||||
// Add change listener
|
||||
advancedSettingsToggle.addEventListener('change', function() {
|
||||
advancedSettings.style.display = this.checked ? 'block' : 'none';
|
||||
setAdvancedFieldsEnabled(this.checked);
|
||||
document.getElementById('advancedEnabled').value = this.checked ? 'true' : 'false';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,594 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{getPdfInfo.title}, header=#{getPdfInfo.header})}"></th:block>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-7 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon other">info</span>
|
||||
<span class="tool-header-text" th:text="#{getPdfInfo.header}"></span>
|
||||
</div>
|
||||
<form id="pdfInfoForm" method="post" enctype="multipart/form-data" th:action="@{'/api/v1/security/get-info-on-pdf'}">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf')}"></div>
|
||||
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{getPdfInfo.submit}"></button>
|
||||
</form>
|
||||
<div class="container mt-0">
|
||||
<!-- PDF Summary section -->
|
||||
<div id="pdf-summary" class="card mt-3 mb-3" style="display: none;">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0" id="summary-heading">PDF Summary</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Quick overview of key details -->
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h6 id="summary-basic-info-heading">Basic Information</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><strong>Pages:</strong> <span id="summary-pages">-</span></li>
|
||||
<li><strong>File Size:</strong> <span id="summary-size">-</span></li>
|
||||
<li><strong>PDF Version:</strong> <span id="summary-version">-</span></li>
|
||||
<li><strong>Language:</strong> <span id="summary-language">-</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6 id="summary-doc-info-heading">Document Information</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><strong>Title:</strong> <span id="summary-title">-</span></li>
|
||||
<li><strong>Author:</strong> <span id="summary-author">-</span></li>
|
||||
<li><strong>Created:</strong> <span id="summary-created">-</span></li>
|
||||
<li><strong>Modified:</strong> <span id="summary-modified">-</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Security section -->
|
||||
<div class="mt-4 mb-3">
|
||||
<h6 id="summary-security-heading">Security Status</h6>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div id="encryption-status" class="card mb-2 h-100">
|
||||
<div class="card-body p-2 d-flex align-items-center">
|
||||
<span id="encryption-icon" class="me-2"><i class="bi bi-lock"></i></span>
|
||||
<span id="encryption-text" class="small">Encryption: Unknown</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div id="permissions-status" class="card mb-2 h-100">
|
||||
<div class="card-body p-2 d-flex align-items-center">
|
||||
<span id="permissions-icon" class="me-2"><i class="bi bi-shield"></i></span>
|
||||
<span id="permissions-text" class="small">Permissions: Unknown</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div id="compliance-status" class="card mb-2 h-100">
|
||||
<div class="card-body p-2 d-flex align-items-center">
|
||||
<span id="compliance-icon" class="me-2"><i class="bi bi-check-circle"></i></span>
|
||||
<span id="compliance-text" class="small">Compliance: Unknown</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detailed alerts -->
|
||||
<div id="summary-alerts" class="mt-3">
|
||||
<!-- Will be populated with detailed alerts for encryption, permissions, etc. -->
|
||||
</div>
|
||||
|
||||
<!-- Descriptive note about PDF characteristics -->
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">PDF Overview</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p id="summary-text" class="mb-0"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Iterate over each main section in the JSON -->
|
||||
<div id="json-content">
|
||||
<!-- JavaScript will populate this section -->
|
||||
</div>
|
||||
|
||||
<!-- Button to download the JSON -->
|
||||
<a href="#" id="downloadJson" class="btn btn-primary mt-3" style="display: none;" th:text="#{getPdfInfo.downloadJson}">Download JSON</a>
|
||||
</div>
|
||||
<script th:src="@{'/js/fetch-utils.js'}"></script>
|
||||
<script th:inline="javascript">
|
||||
// Pre-load message translations
|
||||
const getPdfInfoSummary = /*[[#{getPdfInfo.summary}]]*/ "PDF Summary";
|
||||
const getPdfInfoSummaryEncrypted = /*[[#{getPdfInfo.summary.encrypted}]]*/ "This PDF is encrypted so may face issues with some applications";
|
||||
const getPdfInfoSummaryPermissions = /*[[#{getPdfInfo.summary.permissions}]]*/ "This PDF has {0} restricted permissions which may limit what you can do with it";
|
||||
const getPdfInfoSummaryCompliance = /*[[#{getPdfInfo.summary.compliance}]]*/ "This PDF complies with the {0} standard, meaning it is suitable for {1}";
|
||||
const getPdfInfoSummaryBasicInfo = /*[[#{getPdfInfo.summary.basicInfo}]]*/ "Basic Information";
|
||||
const getPdfInfoSummaryDocInfo = /*[[#{getPdfInfo.summary.docInfo}]]*/ "Document Information";
|
||||
const getPdfInfoSummarySecuritySection = /*[[#{getPdfInfo.summary.security.section}]]*/ "Security Status";
|
||||
const getPdfInfoSummaryEncryptedAlert = /*[[#{getPdfInfo.summary.encrypted.alert}]]*/ "Encrypted PDF - This document is password protected";
|
||||
const getPdfInfoSummaryNotEncryptedAlert = /*[[#{getPdfInfo.summary.not.encrypted.alert}]]*/ "Unencrypted PDF - No password protection";
|
||||
const getPdfInfoSummaryPermissionsAlert = /*[[#{getPdfInfo.summary.permissions.alert}]]*/ "Restricted Permissions - {0} actions are not allowed";
|
||||
const getPdfInfoSummaryAllPermissionsAlert = /*[[#{getPdfInfo.summary.all.permissions.alert}]]*/ "All Permissions Allowed";
|
||||
const getPdfInfoSummaryComplianceAlert = /*[[#{getPdfInfo.summary.compliance.alert}]]*/ "{0} Compliant";
|
||||
const getPdfInfoSummaryNoComplianceAlert = /*[[#{getPdfInfo.summary.no.compliance.alert}]]*/ "No Compliance Standards";
|
||||
|
||||
// Update the summary headings
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('summary-heading').textContent = getPdfInfoSummary;
|
||||
document.getElementById('summary-basic-info-heading').textContent = getPdfInfoSummaryBasicInfo;
|
||||
document.getElementById('summary-doc-info-heading').textContent = getPdfInfoSummaryDocInfo;
|
||||
document.getElementById('summary-security-heading').textContent = getPdfInfoSummarySecuritySection;
|
||||
});
|
||||
|
||||
// Pre-load section descriptions
|
||||
const getPdfInfoSectionBasicInfo = /*[[#{getPdfInfo.section.BasicInfo}]]*/ "Basic Information about the PDF document including file size, page count, and language";
|
||||
const getPdfInfoSectionMetadata = /*[[#{getPdfInfo.section.Metadata}]]*/ "Document metadata including title, author, creation date and other document properties";
|
||||
const getPdfInfoSectionDocumentInfo = /*[[#{getPdfInfo.section.DocumentInfo}]]*/ "Technical details about the PDF document structure and version";
|
||||
const getPdfInfoSectionCompliancy = /*[[#{getPdfInfo.section.Compliancy}]]*/ "PDF standards compliance information (PDF/A, PDF/X, etc.)";
|
||||
const getPdfInfoSectionEncryption = /*[[#{getPdfInfo.section.Encryption}]]*/ "Security and encryption details of the document";
|
||||
const getPdfInfoSectionPermissions = /*[[#{getPdfInfo.section.Permissions}]]*/ "Document permission settings that control what actions can be performed";
|
||||
const getPdfInfoSectionOther = /*[[#{getPdfInfo.section.Other}]]*/ "Additional document components like bookmarks, layers, and embedded files";
|
||||
const getPdfInfoSectionFormFields = /*[[#{getPdfInfo.section.FormFields}]]*/ "Interactive form fields present in the document";
|
||||
const getPdfInfoSectionPerPageInfo = /*[[#{getPdfInfo.section.PerPageInfo}]]*/ "Detailed information about each page in the document";
|
||||
|
||||
document.getElementById("pdfInfoForm").addEventListener("submit", function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
// Clear previous results when submitting a new form
|
||||
document.getElementById('json-content').innerHTML = '';
|
||||
document.getElementById('pdf-summary').style.display = 'none';
|
||||
document.getElementById('downloadJson').style.display = 'none';
|
||||
|
||||
const formData = new FormData(event.target);
|
||||
|
||||
fetchWithCsrf('api/v1/security/get-info-on-pdf', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(response => response.json()).then(data => {
|
||||
// Populate and display the enhanced PDF summary
|
||||
populateSummarySection(data);
|
||||
|
||||
displayJsonData(data);
|
||||
setDownloadLink(data);
|
||||
document.getElementById("downloadJson").style.display = "block";
|
||||
}).catch(error => console.error('Error:', error));
|
||||
|
||||
// Function to reset all summary elements to default state
|
||||
function resetSummaryElements() {
|
||||
// Reset basic information fields
|
||||
document.getElementById('summary-pages').textContent = '-';
|
||||
document.getElementById('summary-size').textContent = '-';
|
||||
document.getElementById('summary-version').textContent = '-';
|
||||
document.getElementById('summary-language').textContent = '-';
|
||||
|
||||
// Reset document information fields
|
||||
document.getElementById('summary-title').textContent = '-';
|
||||
document.getElementById('summary-author').textContent = '-';
|
||||
document.getElementById('summary-created').textContent = '-';
|
||||
document.getElementById('summary-modified').textContent = '-';
|
||||
|
||||
// Reset security status cards
|
||||
const cards = ['encryption-status', 'permissions-status', 'compliance-status'];
|
||||
cards.forEach(id => {
|
||||
const card = document.getElementById(id);
|
||||
// Remove all classes except the base ones
|
||||
card.className = 'card mb-2 h-100';
|
||||
});
|
||||
|
||||
// Reset status text and icons
|
||||
document.getElementById('encryption-icon').innerHTML = '<i class="bi bi-lock"></i>';
|
||||
document.getElementById('encryption-text').textContent = 'Encryption: Unknown';
|
||||
|
||||
document.getElementById('permissions-icon').innerHTML = '<i class="bi bi-shield"></i>';
|
||||
document.getElementById('permissions-text').textContent = 'Permissions: Unknown';
|
||||
|
||||
document.getElementById('compliance-icon').innerHTML = '<i class="bi bi-check-circle"></i>';
|
||||
document.getElementById('compliance-text').textContent = 'Compliance: Unknown';
|
||||
|
||||
// Clear alerts container
|
||||
document.getElementById('summary-alerts').innerHTML = '';
|
||||
|
||||
// Reset summary text
|
||||
document.getElementById('summary-text').innerHTML = '';
|
||||
}
|
||||
|
||||
// Function to populate the enhanced summary section
|
||||
function populateSummarySection(data) {
|
||||
// Reset all elements first
|
||||
resetSummaryElements();
|
||||
|
||||
// Get basic information
|
||||
if (data.BasicInfo) {
|
||||
document.getElementById('summary-pages').textContent = data.BasicInfo["Number of pages"] || "-";
|
||||
|
||||
// Format file size nicely
|
||||
let fileSize = data.BasicInfo["FileSizeInBytes"];
|
||||
if (fileSize) {
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(fileSize) / Math.log(1024));
|
||||
fileSize = (fileSize / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];
|
||||
document.getElementById('summary-size').textContent = fileSize;
|
||||
}
|
||||
|
||||
document.getElementById('summary-language').textContent = data.BasicInfo["Language"] || "-";
|
||||
}
|
||||
|
||||
// Get document information
|
||||
if (data.DocumentInfo) {
|
||||
document.getElementById('summary-version').textContent = data.DocumentInfo["PDF version"] || "-";
|
||||
}
|
||||
|
||||
// Get metadata
|
||||
if (data.Metadata) {
|
||||
document.getElementById('summary-title').textContent = data.Metadata["Title"] || "-";
|
||||
document.getElementById('summary-author').textContent = data.Metadata["Author"] || "-";
|
||||
document.getElementById('summary-created').textContent = data.Metadata["CreationDate"] || "-";
|
||||
document.getElementById('summary-modified').textContent = data.Metadata["ModificationDate"] || "-";
|
||||
}
|
||||
|
||||
// Update security status cards
|
||||
|
||||
// Encryption status
|
||||
const encryptionStatusCard = document.getElementById('encryption-status');
|
||||
const encryptionIcon = document.getElementById('encryption-icon');
|
||||
const encryptionText = document.getElementById('encryption-text');
|
||||
|
||||
if (data.Encryption && data.Encryption.IsEncrypted) {
|
||||
encryptionIcon.innerHTML = '<i class="bi bi-lock-fill"></i>';
|
||||
encryptionText.textContent = getPdfInfoSummaryEncryptedAlert;
|
||||
} else {
|
||||
encryptionIcon.innerHTML = '<i class="bi bi-unlock-fill"></i>';
|
||||
encryptionText.textContent = getPdfInfoSummaryNotEncryptedAlert;
|
||||
}
|
||||
|
||||
// Permissions status
|
||||
const permissionsStatusCard = document.getElementById('permissions-status');
|
||||
const permissionsIcon = document.getElementById('permissions-icon');
|
||||
const permissionsText = document.getElementById('permissions-text');
|
||||
|
||||
let restrictedPermissions = [];
|
||||
if (data.Permissions) {
|
||||
for (const [permission, state] of Object.entries(data.Permissions)) {
|
||||
if (state === "Not Allowed") {
|
||||
restrictedPermissions.push(permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (restrictedPermissions.length > 0) {
|
||||
permissionsIcon.innerHTML = '<i class="bi bi-shield-lock-fill"></i>';
|
||||
const formattedAlert = getPdfInfoSummaryPermissionsAlert.replace('{0}', restrictedPermissions.length);
|
||||
permissionsText.textContent = formattedAlert;
|
||||
} else {
|
||||
permissionsIcon.innerHTML = '<i class="bi bi-shield-check"></i>';
|
||||
permissionsText.textContent = getPdfInfoSummaryAllPermissionsAlert;
|
||||
}
|
||||
|
||||
// Compliance status
|
||||
const complianceStatusCard = document.getElementById('compliance-status');
|
||||
const complianceIcon = document.getElementById('compliance-icon');
|
||||
const complianceText = document.getElementById('compliance-text');
|
||||
|
||||
let hasCompliance = false;
|
||||
let compliantStandards = [];
|
||||
|
||||
if (data.Compliancy) {
|
||||
for (const [standard, compliant] of Object.entries(data.Compliancy)) {
|
||||
if (compliant === true) {
|
||||
hasCompliance = true;
|
||||
const standardName = standard.replace("Is", "").replace("Compliant", "");
|
||||
compliantStandards.push(standardName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasCompliance) {
|
||||
complianceIcon.innerHTML = '<i class="bi bi-check-circle-fill"></i>';
|
||||
const formattedAlert = getPdfInfoSummaryComplianceAlert.replace('{0}', compliantStandards.join(', '));
|
||||
complianceText.textContent = formattedAlert;
|
||||
} else {
|
||||
complianceIcon.innerHTML = '<i class="bi bi-dash-circle"></i>';
|
||||
complianceText.textContent = getPdfInfoSummaryNoComplianceAlert;
|
||||
}
|
||||
|
||||
// Create detailed characteristic alerts
|
||||
const alertsContainer = document.getElementById('summary-alerts');
|
||||
|
||||
// Clear previous alerts
|
||||
alertsContainer.innerHTML = '';
|
||||
|
||||
// Create a single comprehensive security details section
|
||||
let hasSummaryInfo = false;
|
||||
|
||||
// Create a consolidated security details card if there are security details worth highlighting
|
||||
if ((data.Encryption && data.Encryption.IsEncrypted) ||
|
||||
restrictedPermissions.length > 0 ||
|
||||
hasCompliance) {
|
||||
|
||||
const securityDetailsCard = document.createElement('div');
|
||||
securityDetailsCard.className = 'card mt-3 mb-3';
|
||||
|
||||
const cardHeader = document.createElement('div');
|
||||
cardHeader.className = 'card-header';
|
||||
cardHeader.innerHTML = '<h6 class="mb-0">Detailed Security Information</h6>';
|
||||
securityDetailsCard.appendChild(cardHeader);
|
||||
|
||||
const cardBody = document.createElement('div');
|
||||
cardBody.className = 'card-body';
|
||||
|
||||
// Add detailed encryption info
|
||||
if (data.Encryption && data.Encryption.IsEncrypted) {
|
||||
const encryptionDiv = document.createElement('div');
|
||||
encryptionDiv.className = 'mb-3';
|
||||
encryptionDiv.innerHTML = '<h6>Encryption Details:</h6>';
|
||||
|
||||
const encryptionList = document.createElement('ul');
|
||||
encryptionList.className = 'list-unstyled';
|
||||
|
||||
if (data.Encryption.EncryptionAlgorithm) {
|
||||
encryptionList.innerHTML += `<li><strong>Algorithm:</strong> ${data.Encryption.EncryptionAlgorithm}</li>`;
|
||||
}
|
||||
if (data.Encryption.KeyLength) {
|
||||
encryptionList.innerHTML += `<li><strong>Key Length:</strong> ${data.Encryption.KeyLength} bits</li>`;
|
||||
}
|
||||
|
||||
encryptionDiv.appendChild(encryptionList);
|
||||
cardBody.appendChild(encryptionDiv);
|
||||
hasSummaryInfo = true;
|
||||
}
|
||||
|
||||
// Add detailed permissions info
|
||||
if (restrictedPermissions.length > 0) {
|
||||
const permissionsDiv = document.createElement('div');
|
||||
permissionsDiv.className = 'mb-3';
|
||||
permissionsDiv.innerHTML = '<h6>Restricted Permissions:</h6>';
|
||||
|
||||
const permissionsList = document.createElement('ul');
|
||||
restrictedPermissions.forEach(perm => {
|
||||
permissionsList.innerHTML += `<li>${perm}</li>`;
|
||||
});
|
||||
|
||||
permissionsDiv.appendChild(permissionsList);
|
||||
cardBody.appendChild(permissionsDiv);
|
||||
hasSummaryInfo = true;
|
||||
}
|
||||
|
||||
// Add detailed compliance info
|
||||
if (hasCompliance) {
|
||||
const complianceDiv = document.createElement('div');
|
||||
complianceDiv.className = 'mb-3';
|
||||
complianceDiv.innerHTML = '<h6>Standards Compliance:</h6>';
|
||||
|
||||
const complianceList = document.createElement('ul');
|
||||
complianceList.className = 'list-unstyled';
|
||||
|
||||
compliantStandards.forEach(standard => {
|
||||
let standardDescription = '';
|
||||
|
||||
// Add brief descriptions for standards
|
||||
if (standard === "PDF/A") {
|
||||
standardDescription = 'ISO standard for long-term document archiving';
|
||||
} else if (standard === "PDF/X") {
|
||||
standardDescription = 'ISO standard for printing and graphic arts exchange';
|
||||
} else if (standard === "PDF/UA") {
|
||||
standardDescription = 'ISO standard for universal accessibility';
|
||||
} else if (standard === "PDF/E") {
|
||||
standardDescription = 'ISO standard for engineering documents';
|
||||
} else if (standard === "PDF/VT") {
|
||||
standardDescription = 'ISO standard for variable and transactional printing';
|
||||
}
|
||||
|
||||
complianceList.innerHTML += `<li><strong>${standard}:</strong> ${standardDescription}</li>`;
|
||||
});
|
||||
|
||||
complianceDiv.appendChild(complianceList);
|
||||
cardBody.appendChild(complianceDiv);
|
||||
hasSummaryInfo = true;
|
||||
}
|
||||
|
||||
securityDetailsCard.appendChild(cardBody);
|
||||
|
||||
if (hasSummaryInfo) {
|
||||
alertsContainer.appendChild(securityDetailsCard);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a general document summary
|
||||
const summaryTextElement = document.getElementById('summary-text');
|
||||
|
||||
// Create a general summary for the document
|
||||
let generalSummary = `This is a ${data.BasicInfo["Number of pages"] || "multi"}-page PDF`;
|
||||
|
||||
if (data.Metadata && data.Metadata["Title"]) {
|
||||
generalSummary += ` titled "${data.Metadata["Title"]}"`;
|
||||
}
|
||||
|
||||
if (data.Metadata && data.Metadata["Author"]) {
|
||||
generalSummary += ` created by ${data.Metadata["Author"]}`;
|
||||
}
|
||||
|
||||
if (data.DocumentInfo && data.DocumentInfo["PDF version"]) {
|
||||
generalSummary += ` (PDF version ${data.DocumentInfo["PDF version"]})`;
|
||||
}
|
||||
|
||||
// Add security information to the general summary if relevant
|
||||
if (data.Encryption && data.Encryption.IsEncrypted) {
|
||||
generalSummary += '. The document is password protected';
|
||||
|
||||
if (data.Encryption.EncryptionAlgorithm) {
|
||||
generalSummary += ` using ${data.Encryption.EncryptionAlgorithm}`;
|
||||
|
||||
if (data.Encryption.KeyLength) {
|
||||
generalSummary += ` (${data.Encryption.KeyLength} bit)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (restrictedPermissions.length > 0) {
|
||||
generalSummary += `. It has ${restrictedPermissions.length} restricted permissions`;
|
||||
}
|
||||
|
||||
// Add compliance standards if available
|
||||
if (hasCompliance && compliantStandards.length > 0) {
|
||||
generalSummary += `. This document complies with the ${compliantStandards.join(', ')} PDF standard${compliantStandards.length > 1 ? 's' : ''}`;
|
||||
}
|
||||
|
||||
generalSummary += '.';
|
||||
|
||||
// Remove SummaryData from JSON to avoid duplication
|
||||
if (data.SummaryData) {
|
||||
delete data.SummaryData;
|
||||
}
|
||||
|
||||
summaryTextElement.innerHTML = generalSummary;
|
||||
|
||||
// Display the summary section
|
||||
document.getElementById('pdf-summary').style.display = 'block';
|
||||
}
|
||||
|
||||
function generateSummaryFromData(summaryData) {
|
||||
let summary = [];
|
||||
|
||||
// Handle encryption information
|
||||
if (summaryData.encrypted) {
|
||||
summary.push(getPdfInfoSummaryEncrypted);
|
||||
}
|
||||
|
||||
// Handle permissions information
|
||||
if (summaryData.restrictedPermissions && summaryData.restrictedPermissions.length > 0) {
|
||||
const formattedPermissionsText = getPdfInfoSummaryPermissions.replace('{0}', summaryData.restrictedPermissionsCount);
|
||||
summary.push(formattedPermissionsText);
|
||||
}
|
||||
|
||||
// Handle standard compliance information
|
||||
if (summaryData.standardCompliance) {
|
||||
const formattedComplianceText = getPdfInfoSummaryCompliance
|
||||
.replace('{0}', summaryData.standardCompliance);
|
||||
summary.push(formattedComplianceText);
|
||||
}
|
||||
|
||||
return summary.join(' ');
|
||||
}
|
||||
});
|
||||
|
||||
function displayJsonData(jsonData) {
|
||||
const jsonContent = document.getElementById('json-content');
|
||||
|
||||
while (jsonContent.firstChild) {
|
||||
jsonContent.removeChild(jsonContent.firstChild);
|
||||
}
|
||||
|
||||
for (const key in jsonData) {
|
||||
const sectionElem = createJsonSection(key, jsonData[key]);
|
||||
jsonContent.appendChild(sectionElem);
|
||||
}
|
||||
}
|
||||
|
||||
function setDownloadLink(jsonData) {
|
||||
const downloadLink = document.getElementById('downloadJson');
|
||||
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(jsonData, null, 4));
|
||||
downloadLink.setAttribute("href", dataStr);
|
||||
downloadLink.setAttribute("download", "data.json");
|
||||
}
|
||||
|
||||
function createJsonSection(key, value, depth = 0) {
|
||||
let safeKey = (typeof key === "string") ? key.replace(/[^a-zA-Z0-9]/g, '_') : key;
|
||||
const card = document.createElement('div');
|
||||
card.className = 'card mb-3';
|
||||
|
||||
const header = document.createElement('div');
|
||||
header.className = 'card-header';
|
||||
header.id = `${safeKey}-heading-${depth}`;
|
||||
const h5Elem = document.createElement('h5');
|
||||
h5Elem.className = 'mb-0 d-flex align-items-center';
|
||||
|
||||
// Create the main content (button or text)
|
||||
if (key === 'XMPMetadata' && typeof value === "string") {
|
||||
const buttonElem = createButtonElement(key, safeKey, depth);
|
||||
h5Elem.appendChild(buttonElem);
|
||||
} else if (value && typeof value === 'object') {
|
||||
if (Array.isArray(value) && value.length === 0) {
|
||||
h5Elem.textContent = `${key}: Empty array`;
|
||||
} else if (!Array.isArray(value) && Object.keys(value).length === 0) {
|
||||
h5Elem.textContent = `${key}: Empty object`;
|
||||
} else {
|
||||
const buttonElem = createButtonElement(key, safeKey, depth);
|
||||
h5Elem.appendChild(buttonElem);
|
||||
}
|
||||
} else {
|
||||
h5Elem.textContent = `${key}: ${String(value)}`;
|
||||
}
|
||||
|
||||
// Info buttons removed as requested
|
||||
|
||||
header.appendChild(h5Elem);
|
||||
card.appendChild(header);
|
||||
|
||||
const content = document.createElement('div');
|
||||
content.id = `${safeKey}-content-${depth}`;
|
||||
content.className = 'collapse';
|
||||
content.setAttribute('aria-labelledby', `${safeKey}-heading-${depth}`);
|
||||
|
||||
if (key === 'XMPMetadata' && typeof value === "string") {
|
||||
const body = document.createElement('div');
|
||||
body.className = 'card-body';
|
||||
const preElem = document.createElement('pre');
|
||||
preElem.textContent = value; // Not escaping since we're using textContent
|
||||
body.appendChild(preElem);
|
||||
content.appendChild(body);
|
||||
} else if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
const body = document.createElement('div');
|
||||
body.className = 'card-body';
|
||||
for (const subKey in value) {
|
||||
const subElem = createJsonSection(subKey, value[subKey], depth + 1);
|
||||
body.appendChild(subElem);
|
||||
}
|
||||
content.appendChild(body);
|
||||
} else if (value && typeof value === 'object' && Array.isArray(value)) {
|
||||
const body = document.createElement('div');
|
||||
body.className = 'card-body';
|
||||
value.forEach((val, index) => {
|
||||
const subElem = createJsonSection(`${key}[${index}]`, val, depth + 1);
|
||||
body.appendChild(subElem);
|
||||
});
|
||||
content.appendChild(body);
|
||||
}
|
||||
|
||||
card.appendChild(content);
|
||||
return card;
|
||||
}
|
||||
function createButtonElement(key, safeKey, depth) {
|
||||
const buttonElem = document.createElement('button');
|
||||
buttonElem.className = 'btn btn-link';
|
||||
buttonElem.type = 'button';
|
||||
buttonElem.dataset.bsToggle = "collapse";
|
||||
buttonElem.dataset.bsTarget = `#${safeKey}-content-${depth}`;
|
||||
buttonElem.setAttribute('aria-expanded', 'true');
|
||||
buttonElem.setAttribute('aria-controls', `${safeKey}-content-${depth}`);
|
||||
buttonElem.textContent = key;
|
||||
return buttonElem;
|
||||
}
|
||||
const collapsibleElems = document.querySelectorAll('[data-bs-toggle="collapse"]');
|
||||
collapsibleElems.forEach(elem => {
|
||||
new bootstrap.Collapse(elem);
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
197
stirling-pdf/.gitignore
vendored
Normal file
197
stirling-pdf/.gitignore
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
### 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
|
||||
/stirling-pdf/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
|
95
stirling-pdf/build.gradle
Normal file
95
stirling-pdf/build.gradle
Normal file
@ -0,0 +1,95 @@
|
||||
repositories {
|
||||
maven { url = 'https://build.shibboleth.net/maven/releases' }
|
||||
maven { url = 'https://maven.pkg.github.com/jcefmaven/jcefmaven' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
if (System.getenv('STIRLING_PDF_DESKTOP_UI') != 'false'
|
||||
|| (project.hasProperty('STIRLING_PDF_DESKTOP_UI')
|
||||
&& project.getProperty('STIRLING_PDF_DESKTOP_UI') != 'false')) {
|
||||
implementation 'me.friwi:jcefmaven:132.3.1'
|
||||
implementation 'org.openjfx:javafx-controls:21'
|
||||
implementation 'org.openjfx:javafx-swing:21'
|
||||
}
|
||||
|
||||
if (System.getenv('DISABLE_ADDITIONAL_FEATURES') != 'true'
|
||||
|| (project.hasProperty('DISABLE_ADDITIONAL_FEATURES')
|
||||
&& System.getProperty('DISABLE_ADDITIONAL_FEATURES') != 'true')) {
|
||||
implementation project(':proprietary')
|
||||
}
|
||||
|
||||
implementation project(':common')
|
||||
implementation 'org.springframework.boot:spring-boot-starter-jetty'
|
||||
implementation 'com.posthog.java:posthog:1.2.0'
|
||||
implementation 'commons-io:commons-io:2.19.0'
|
||||
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
|
||||
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
|
||||
implementation 'io.micrometer:micrometer-core:1.14.6'
|
||||
implementation 'com.google.zxing:core:3.5.3'
|
||||
implementation "org.commonmark:commonmark:$commonmarkVersion" // https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||
implementation "org.commonmark:commonmark-ext-gfm-tables:$commonmarkVersion"
|
||||
|
||||
// General PDF dependencies
|
||||
implementation "org.apache.pdfbox:preflight:$pdfboxVersion"
|
||||
implementation "org.apache.pdfbox:xmpbox:$pdfboxVersion"
|
||||
|
||||
// https://mvnrepository.com/artifact/technology.tabula/tabula
|
||||
implementation ('technology.tabula:tabula:1.0.5') {
|
||||
exclude group: 'org.slf4j', module: 'slf4j-simple'
|
||||
exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on'
|
||||
exclude group: 'com.google.code.gson', module: 'gson'
|
||||
}
|
||||
implementation 'org.apache.pdfbox:jbig2-imageio:3.0.4'
|
||||
implementation 'com.opencsv:opencsv:5.11' // https://mvnrepository.com/artifact/com.opencsv/opencsv
|
||||
|
||||
// Batik
|
||||
implementation 'org.apache.xmlgraphics:batik-all:1.18'
|
||||
|
||||
// TwelveMonkeys
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-bmp:$imageioVersion"
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-jpeg:$imageioVersion"
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-tiff:$imageioVersion"
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-webp:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-hdr:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-icns:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-iff:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pcx:$imageioVersion@
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pict:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pnm:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-psd:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-sgi:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-tga:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-thumbsdb:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-xwd:$imageioVersion"
|
||||
|
||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources {
|
||||
srcDirs += ['../configs']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
zip64 = true
|
||||
|
||||
from {
|
||||
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
|
||||
manifest {
|
||||
attributes(
|
||||
'Main-Class': 'stirling.software.SPDF.SPDFApplication',
|
||||
'Implementation-Title': 'Stirling-PDF',
|
||||
'Implementation-Version': project.version
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
jar.dependsOn ':common:jar'
|
||||
jar.dependsOn ':proprietary:jar'
|
@ -11,7 +11,6 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
@ -27,6 +26,7 @@ import jakarta.annotation.PreDestroy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.UI.WebBrowser;
|
||||
import stirling.software.common.configuration.AppConfig;
|
||||
import stirling.software.common.configuration.ConfigInitializer;
|
||||
import stirling.software.common.configuration.InstallationPathConfig;
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
@ -36,9 +36,9 @@ import stirling.software.common.util.UrlUtils;
|
||||
@EnableScheduling
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = {
|
||||
"stirling.software.common",
|
||||
"stirling.software.SPDF",
|
||||
"stirling.software.proprietary.security"
|
||||
"stirling.software.common",
|
||||
"stirling.software.proprietary"
|
||||
},
|
||||
exclude = {
|
||||
DataSourceAutoConfiguration.class,
|
||||
@ -50,20 +50,17 @@ public class SPDFApplication {
|
||||
private static String baseUrlStatic;
|
||||
private static String contextPathStatic;
|
||||
|
||||
private final AppConfig appConfig;
|
||||
private final Environment env;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final WebBrowser webBrowser;
|
||||
|
||||
@Value("${baseUrl:http://localhost}")
|
||||
private String baseUrl;
|
||||
|
||||
@Value("${server.servlet.context-path:/}")
|
||||
private String contextPath;
|
||||
|
||||
public SPDFApplication(
|
||||
AppConfig appConfig,
|
||||
Environment env,
|
||||
ApplicationProperties applicationProperties,
|
||||
@Autowired(required = false) WebBrowser webBrowser) {
|
||||
this.appConfig = appConfig;
|
||||
this.env = env;
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.webBrowser = webBrowser;
|
||||
@ -152,9 +149,14 @@ public class SPDFApplication {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
baseUrlStatic = this.baseUrl;
|
||||
contextPathStatic = this.contextPath;
|
||||
String baseUrl = appConfig.getBaseUrl();
|
||||
String contextPath = appConfig.getContextPath();
|
||||
String serverPort = appConfig.getServerPort();
|
||||
baseUrlStatic = baseUrl;
|
||||
contextPathStatic = contextPath;
|
||||
serverPortStatic = serverPort;
|
||||
String url = baseUrl + ":" + getStaticPort() + contextPath;
|
||||
|
||||
if (webBrowser != null
|
||||
&& Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
|
||||
webBrowser.initWebUI(url);
|
||||
@ -165,6 +167,7 @@ public class SPDFApplication {
|
||||
try {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
|
||||
if (os.contains("win")) {
|
||||
// For Windows
|
||||
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
|
||||
@ -181,17 +184,6 @@ public class SPDFApplication {
|
||||
log.info("Running configs {}", applicationProperties.toString());
|
||||
}
|
||||
|
||||
@Value("${server.port:8080}")
|
||||
public void setServerPort(String port) {
|
||||
if ("auto".equalsIgnoreCase(port)) {
|
||||
// Use Spring Boot's automatic port assignment (server.port=0)
|
||||
SPDFApplication.serverPortStatic =
|
||||
"0"; // This will let Spring Boot assign an available port
|
||||
} else {
|
||||
SPDFApplication.serverPortStatic = port;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setServerPortStatic(String port) {
|
||||
if ("auto".equalsIgnoreCase(port)) {
|
||||
// Use Spring Boot's automatic port assignment (server.port=0)
|
@ -8,13 +8,14 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.service.EndpointConfigurationService;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class EndpointInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final EndpointConfiguration endpointConfiguration;
|
||||
private final EndpointConfigurationService endpointConfigurationService;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(
|
||||
@ -37,10 +38,10 @@ public class EndpointInterceptor implements HandlerInterceptor {
|
||||
}
|
||||
|
||||
log.debug("Request endpoint: {}", requestEndpoint);
|
||||
isEnabled = endpointConfiguration.isEndpointEnabled(requestEndpoint);
|
||||
isEnabled = endpointConfigurationService.isEndpointEnabled(requestEndpoint);
|
||||
log.debug("Is endpoint enabled: {}", isEnabled);
|
||||
} else {
|
||||
isEnabled = endpointConfiguration.isEndpointEnabled(requestURI);
|
||||
isEnabled = endpointConfigurationService.isEndpointEnabled(requestURI);
|
||||
}
|
||||
|
||||
if (!isEnabled) {
|
@ -13,20 +13,21 @@ import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.configuration.RuntimePathConfig;
|
||||
import stirling.software.SPDF.service.EndpointConfigurationService;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class ExternalAppDepConfig {
|
||||
|
||||
private final EndpointConfiguration endpointConfiguration;
|
||||
private final EndpointConfigurationService endpointConfigurationService;
|
||||
|
||||
private final String weasyprintPath;
|
||||
private final String unoconvPath;
|
||||
private final Map<String, List<String>> commandToGroupMapping;
|
||||
|
||||
public ExternalAppDepConfig(
|
||||
EndpointConfiguration endpointConfiguration, RuntimePathConfig runtimePathConfig) {
|
||||
this.endpointConfiguration = endpointConfiguration;
|
||||
EndpointConfigurationService endpointConfigurationService, RuntimePathConfig runtimePathConfig) {
|
||||
this.endpointConfigurationService = endpointConfigurationService;
|
||||
weasyprintPath = runtimePathConfig.getWeasyPrintPath();
|
||||
unoconvPath = runtimePathConfig.getUnoConvertPath();
|
||||
|
||||
@ -62,7 +63,7 @@ public class ExternalAppDepConfig {
|
||||
}
|
||||
|
||||
private List<String> getAffectedFeatures(String group) {
|
||||
return endpointConfiguration.getEndpointsForGroup(group).stream()
|
||||
return endpointConfigurationService.getEndpointsForGroup(group).stream()
|
||||
.map(endpoint -> formatEndpointAsFeature(endpoint))
|
||||
.toList();
|
||||
}
|
||||
@ -93,7 +94,7 @@ public class ExternalAppDepConfig {
|
||||
if (affectedGroups != null) {
|
||||
for (String group : affectedGroups) {
|
||||
List<String> affectedFeatures = getAffectedFeatures(group);
|
||||
endpointConfiguration.disableGroup(group);
|
||||
endpointConfigurationService.disableGroup(group);
|
||||
log.warn(
|
||||
"Missing dependency: {} - Disabling group: {} (Affected features: {})",
|
||||
command,
|
||||
@ -120,8 +121,8 @@ public class ExternalAppDepConfig {
|
||||
if (!pythonAvailable) {
|
||||
List<String> pythonFeatures = getAffectedFeatures("Python");
|
||||
List<String> openCVFeatures = getAffectedFeatures("OpenCV");
|
||||
endpointConfiguration.disableGroup("Python");
|
||||
endpointConfiguration.disableGroup("OpenCV");
|
||||
endpointConfigurationService.disableGroup("Python");
|
||||
endpointConfigurationService.disableGroup("OpenCV");
|
||||
log.warn(
|
||||
"Missing dependency: Python - Disabling Python features: {} and OpenCV features: {}",
|
||||
String.join(", ", pythonFeatures),
|
||||
@ -139,20 +140,20 @@ public class ExternalAppDepConfig {
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0) {
|
||||
List<String> openCVFeatures = getAffectedFeatures("OpenCV");
|
||||
endpointConfiguration.disableGroup("OpenCV");
|
||||
endpointConfigurationService.disableGroup("OpenCV");
|
||||
log.warn(
|
||||
"OpenCV not available in Python - Disabling OpenCV features: {}",
|
||||
String.join(", ", openCVFeatures));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
List<String> openCVFeatures = getAffectedFeatures("OpenCV");
|
||||
endpointConfiguration.disableGroup("OpenCV");
|
||||
endpointConfigurationService.disableGroup("OpenCV");
|
||||
log.warn(
|
||||
"Error checking OpenCV: {} - Disabling OpenCV features: {}",
|
||||
e.getMessage(),
|
||||
String.join(", ", openCVFeatures));
|
||||
}
|
||||
}
|
||||
endpointConfiguration.logDisabledEndpointsSummary();
|
||||
endpointConfigurationService.logDisabledEndpointsSummary();
|
||||
}
|
||||
}
|
@ -59,7 +59,7 @@ public class InitialSetup {
|
||||
|
||||
public void initEnableCSRFSecurity() throws IOException {
|
||||
if (GeneralUtils.isVersionHigher(
|
||||
"0.36.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) {
|
||||
"0.46.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) {
|
||||
Boolean csrf = applicationProperties.getSecurity().getCsrfDisabled();
|
||||
if (!csrf) {
|
||||
GeneralUtils.saveKeyToSettings("security.csrfDisabled", false);
|
@ -25,7 +25,11 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||
// Handler for external static resources
|
||||
registry.addResourceHandler("/**")
|
||||
.addResourceLocations(
|
||||
"file:" + InstallationPathConfig.getStaticPath(), "classpath:/static/");
|
||||
"file:" + InstallationPathConfig.getStaticPath(),
|
||||
"classpath:/static/"
|
||||
);
|
||||
registry.addResourceHandler("/js/**").addResourceLocations("classpath:/static/js/");
|
||||
registry.addResourceHandler("/css/**").addResourceLocations("classpath:/static/css/");
|
||||
// .setCachePeriod(0); // Optional: disable caching
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
// import jakarta.servlet.http.HttpServletResponse;
|
||||
// import jakarta.servlet.http.HttpSession;
|
||||
// import lombok.extern.slf4j.Slf4j;
|
||||
// import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
// import stirling.software.common.util.RequestUriUtils;
|
||||
//
|
||||
//// @Component
|
||||
// @Slf4j
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user