mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-23 16:05:09 +00:00
Compare commits
1 Commits
0d36e74276
...
02f704db73
Author | SHA1 | Date | |
---|---|---|---|
![]() |
02f704db73 |
14
.gitattributes
vendored
14
.gitattributes
vendored
@ -1,10 +1,10 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
# Ignore all JavaScript files in a directory
|
||||
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
|
||||
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
|
||||
|
33
.github/actions/setup-bot/action.yml
vendored
33
.github/actions/setup-bot/action.yml
vendored
@ -1,33 +0,0 @@
|
||||
name: 'Setup GitHub App Bot'
|
||||
description: 'Generates a GitHub App Token and configures Git for a bot'
|
||||
inputs:
|
||||
app-id:
|
||||
description: 'GitHub App ID'
|
||||
required: True
|
||||
private-key:
|
||||
description: 'GitHub App Private Key'
|
||||
required: True
|
||||
outputs:
|
||||
token:
|
||||
description: 'Generated GitHub App Token'
|
||||
value: ${{ steps.generate-token.outputs.token }}
|
||||
committer:
|
||||
description: 'Committer string for Git'
|
||||
value: "${{ steps.generate-token.outputs.app-slug }}[bot] <${{ steps.generate-token.outputs.app-slug }}[bot]@users.noreply.github.com>"
|
||||
app-slug:
|
||||
description: 'GitHub App slug'
|
||||
value: ${{ steps.generate-token.outputs.app-slug }}
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Generate a GitHub App Token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||
with:
|
||||
app-id: ${{ inputs.app-id }}
|
||||
private-key: ${{ inputs.private-key }}
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global user.name "${{ steps.generate-token.outputs.app-slug }}[bot]"
|
||||
git config --global user.email "${{ steps.generate-token.outputs.app-slug }}[bot]@users.noreply.github.com"
|
||||
shell: bash
|
57
.github/labeler-config.yml
vendored
57
.github/labeler-config.yml
vendored
@ -1,45 +1,60 @@
|
||||
Translation:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/resources/messages_*_*.properties'
|
||||
- any-glob-to-any-file: 'src/main/resources/messages_*_*.properties'
|
||||
- any-glob-to-any-file: 'scripts/ignore_translation.toml'
|
||||
- any-glob-to-any-file: 'stirling-pdf/src/main/resources/templates/fragments/languages.html'
|
||||
- any-glob-to-any-file: 'src/main/resources/templates/fragments/languages.html'
|
||||
|
||||
Front End:
|
||||
- changed-files:
|
||||
- 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/**/*'
|
||||
- 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/**/*'
|
||||
|
||||
Java:
|
||||
- changed-files:
|
||||
- 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'
|
||||
- any-glob-to-any-file: 'src/main/java/**/*.java'
|
||||
|
||||
Back End:
|
||||
- changed-files:
|
||||
- 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: '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: 'scripts/png_to_webp.py'
|
||||
- any-glob-to-any-file: 'split_photos.py'
|
||||
|
||||
Security:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'proprietary/src/main/java/stirling/software/proprietary/security/**/*'
|
||||
- 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: '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: '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: '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: 'scripts/png_to_webp.py'
|
||||
- any-glob-to-any-file: 'split_photos.py'
|
||||
- any-glob-to-any-file: '.github/workflows/swagger.yml'
|
||||
@ -73,9 +88,7 @@ Devtools:
|
||||
Test:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'cucumber/**/*'
|
||||
- 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/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'
|
||||
|
26
.github/scripts/check_language_properties.py
vendored
26
.github/scripts/check_language_properties.py
vendored
@ -196,9 +196,7 @@ def check_for_differences(reference_file, file_list, branch, actor):
|
||||
|
||||
if len(file_list) == 1:
|
||||
file_arr = file_list[0].split()
|
||||
base_dir = os.path.abspath(
|
||||
os.path.join(os.getcwd(), "stirling-pdf", "src", "main", "resources")
|
||||
)
|
||||
base_dir = os.path.abspath(os.path.join(os.getcwd(), "src", "main", "resources"))
|
||||
|
||||
for file_path in file_arr:
|
||||
file_normpath = os.path.normpath(file_path)
|
||||
@ -218,19 +216,10 @@ def check_for_differences(reference_file, file_list, branch, actor):
|
||||
or (
|
||||
# only local windows command
|
||||
not file_normpath.startswith(
|
||||
os.path.join(
|
||||
"", "stirling-pdf", "src", "main", "resources", "messages_"
|
||||
)
|
||||
os.path.join("", "src", "main", "resources", "messages_")
|
||||
)
|
||||
and not file_normpath.startswith(
|
||||
os.path.join(
|
||||
os.getcwd(),
|
||||
"stirling-pdf",
|
||||
"src",
|
||||
"main",
|
||||
"resources",
|
||||
"messages_",
|
||||
)
|
||||
os.path.join(os.getcwd(), "src", "main", "resources", "messages_")
|
||||
)
|
||||
)
|
||||
or not file_normpath.endswith(".properties")
|
||||
@ -328,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/stirling-pdf/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/src/main/resources/messages_en_GB.properties)"
|
||||
)
|
||||
else:
|
||||
report.append("## ✅ Overall Check Status: **_Success_**")
|
||||
@ -388,12 +377,7 @@ if __name__ == "__main__":
|
||||
else:
|
||||
file_list = glob.glob(
|
||||
os.path.join(
|
||||
os.getcwd(),
|
||||
"stirling-pdf",
|
||||
"src",
|
||||
"main",
|
||||
"resources",
|
||||
"messages_*.properties",
|
||||
os.getcwd(), "src", "main", "resources", "messages_*.properties"
|
||||
)
|
||||
)
|
||||
update_missing_keys(args.reference_file, file_list)
|
||||
|
22
.github/workflows/PR-Demo-Comment-with-react.yml
vendored
22
.github/workflows/PR-Demo-Comment-with-react.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
pr_repository: ${{ steps.get-pr-info.outputs.repository }}
|
||||
pr_ref: ${{ steps.get-pr-info.outputs.ref }}
|
||||
comment_id: ${{ github.event.comment.id }}
|
||||
disable_security: ${{ steps.check-security-flag.outputs.disable_security }}
|
||||
enable_security: ${{ steps.check-security-flag.outputs.enable_security }}
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
@ -84,7 +84,7 @@ jobs:
|
||||
|
||||
core.setOutput('repository', repository);
|
||||
core.setOutput('ref', pr.head.ref);
|
||||
|
||||
|
||||
- name: Check for security/login flag
|
||||
id: check-security-flag
|
||||
env:
|
||||
@ -92,10 +92,10 @@ jobs:
|
||||
run: |
|
||||
if [[ "$COMMENT_BODY" == *"security"* ]] || [[ "$COMMENT_BODY" == *"login"* ]]; then
|
||||
echo "Security flags detected in comment"
|
||||
echo "disable_security=false" >> $GITHUB_OUTPUT
|
||||
echo "enable_security=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "No security flags detected in comment"
|
||||
echo "disable_security=true" >> $GITHUB_OUTPUT
|
||||
echo "enable_security=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Add 'in_progress' reaction to comment
|
||||
@ -155,10 +155,10 @@ jobs:
|
||||
|
||||
- name: Run Gradle Command
|
||||
run: |
|
||||
if [ "${{ needs.check-comment.outputs.disable_security }}" == "true" ]; then
|
||||
export DISABLE_ADDITIONAL_FEATURES=true
|
||||
if [ "${{ needs.check-comment.outputs.enable_security }}" == "true" ]; then
|
||||
export DOCKER_ENABLE_SECURITY=true
|
||||
else
|
||||
export DISABLE_ADDITIONAL_FEATURES=false
|
||||
export DOCKER_ENABLE_SECURITY=false
|
||||
fi
|
||||
./gradlew clean build
|
||||
env:
|
||||
@ -180,7 +180,7 @@ jobs:
|
||||
password: ${{ secrets.DOCKER_HUB_API }}
|
||||
|
||||
- name: Build and push PR-specific image
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
@ -199,7 +199,7 @@ jobs:
|
||||
id: deploy
|
||||
run: |
|
||||
# Set security settings based on flags
|
||||
if [ "${{ needs.check-comment.outputs.disable_security }}" == "false" ]; then
|
||||
if [ "${{ needs.check-comment.outputs.enable_security }}" == "true" ]; then
|
||||
DOCKER_SECURITY="true"
|
||||
LOGIN_SECURITY="true"
|
||||
SECURITY_STATUS="🔒 Security Enabled"
|
||||
@ -223,7 +223,7 @@ jobs:
|
||||
- /stirling/PR-${{ needs.check-comment.outputs.pr_number }}/config:/configs:rw
|
||||
- /stirling/PR-${{ needs.check-comment.outputs.pr_number }}/logs:/logs:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "${DOCKER_SECURITY}"
|
||||
DOCKER_ENABLE_SECURITY: "${DOCKER_SECURITY}"
|
||||
SECURITY_ENABLELOGIN: "${LOGIN_SECURITY}"
|
||||
SYSTEM_DEFAULTLOCALE: en-GB
|
||||
UI_APPNAME: "Stirling-PDF PR#${{ needs.check-comment.outputs.pr_number }}"
|
||||
@ -250,7 +250,7 @@ jobs:
|
||||
docker-compose pull
|
||||
docker-compose up -d
|
||||
ENDSSH
|
||||
|
||||
|
||||
# Set output for use in PR comment
|
||||
echo "security_status=${SECURITY_STATUS}" >> $GITHUB_ENV
|
||||
|
||||
|
53
.github/workflows/build.yml
vendored
53
.github/workflows/build.yml
vendored
@ -40,63 +40,22 @@ jobs:
|
||||
- name: Build with Gradle and no spring security
|
||||
run: ./gradlew clean build
|
||||
env:
|
||||
DISABLE_ADDITIONAL_FEATURES: true
|
||||
DOCKER_ENABLE_SECURITY: false
|
||||
|
||||
- name: Build with Gradle and with spring security
|
||||
run: ./gradlew clean build
|
||||
env:
|
||||
DISABLE_ADDITIONAL_FEATURES: false
|
||||
|
||||
- name: Check Test Reports Exist
|
||||
id: check-reports
|
||||
if: always()
|
||||
run: |
|
||||
missing_reports=()
|
||||
|
||||
# Check for required test report directories
|
||||
if [ ! -d "stirling-pdf/build/reports/tests/" ]; then
|
||||
missing_reports+=("stirling-pdf/build/reports/tests/")
|
||||
fi
|
||||
if [ ! -d "stirling-pdf/build/test-results/" ]; then
|
||||
missing_reports+=("stirling-pdf/build/test-results/")
|
||||
fi
|
||||
if [ ! -d "common/build/reports/tests/" ]; then
|
||||
missing_reports+=("common/build/reports/tests/")
|
||||
fi
|
||||
if [ ! -d "common/build/test-results/" ]; then
|
||||
missing_reports+=("common/build/test-results/")
|
||||
fi
|
||||
if [ ! -d "proprietary/build/reports/tests/" ]; then
|
||||
missing_reports+=("proprietary/build/reports/tests/")
|
||||
fi
|
||||
if [ ! -d "proprietary/build/test-results/" ]; then
|
||||
missing_reports+=("proprietary/build/test-results/")
|
||||
fi
|
||||
|
||||
# Fail if any required reports are missing
|
||||
if [ ${#missing_reports[@]} -gt 0 ]; then
|
||||
echo "ERROR: The following required test report directories are missing:"
|
||||
printf '%s\n' "${missing_reports[@]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All required test report directories are present"
|
||||
DOCKER_ENABLE_SECURITY: true
|
||||
|
||||
- name: Upload Test Reports
|
||||
if: steps.check-reports.outcome == 'success'
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: test-reports-jdk-${{ matrix.jdk-version }}
|
||||
path: |
|
||||
stirling-pdf/build/reports/tests/
|
||||
stirling-pdf/build/test-results/
|
||||
stirling-pdf/build/reports/problems/
|
||||
common/build/reports/tests/
|
||||
common/build/test-results/
|
||||
common/build/reports/problems/
|
||||
proprietary/build/reports/tests/
|
||||
proprietary/build/test-results/
|
||||
proprietary/build/reports/problems/
|
||||
build/reports/tests/
|
||||
build/test-results/
|
||||
build/reports/problems/
|
||||
retention-days: 3
|
||||
|
||||
check-licence:
|
||||
|
42
.github/workflows/check_properties.yml
vendored
42
.github/workflows/check_properties.yml
vendored
@ -4,7 +4,7 @@ on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "stirling-pdf/src/main/resources/messages_*.properties"
|
||||
- "src/main/resources/messages_*.properties"
|
||||
|
||||
permissions:
|
||||
contents: read # Allow read access to repository content
|
||||
@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write # Allow posting comments on issues/PRs
|
||||
pull-requests: write # Allow writing to pull requests
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||
@ -25,18 +25,15 @@ jobs:
|
||||
- name: Checkout main branch first
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Setup GitHub App Bot
|
||||
id: setup-bot
|
||||
uses: ./.github/actions/setup-bot
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Get PR data
|
||||
id: get-pr-data
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
github-token: ${{ steps.setup-bot.outputs.token }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const repoOwner = context.payload.repository.owner.login;
|
||||
@ -57,30 +54,16 @@ jobs:
|
||||
- name: Fetch PR changed files
|
||||
id: fetch-pr-changes
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.setup-bot.outputs.token }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
echo "Fetching PR changed files..."
|
||||
echo "Getting list of changed files from PR..."
|
||||
# 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"
|
||||
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
|
||||
|
||||
- name: Determine reference file test
|
||||
id: determine-file
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
github-token: ${{ steps.setup-bot.outputs.token }}
|
||||
script: |
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
@ -116,7 +99,7 @@ jobs:
|
||||
// Filter for relevant files based on the PR changes
|
||||
const changedFiles = files
|
||||
.map(file => file.filename)
|
||||
.filter(file => /^stirling-pdf\/src\/main\/resources\/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$/.test(file));
|
||||
.filter(file => /^src\/main\/resources\/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$/.test(file));
|
||||
|
||||
console.log("Changed files:", changedFiles);
|
||||
|
||||
@ -154,12 +137,12 @@ jobs:
|
||||
|
||||
// Determine reference file
|
||||
let referenceFilePath;
|
||||
if (changedFiles.includes("stirling-pdf/src/main/resources/messages_en_GB.properties")) {
|
||||
if (changedFiles.includes("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: "stirling-pdf/src/main/resources/messages_en_GB.properties",
|
||||
path: "src/main/resources/messages_en_GB.properties",
|
||||
ref: branch,
|
||||
});
|
||||
|
||||
@ -171,7 +154,7 @@ jobs:
|
||||
const { data: fileContent } = await github.rest.repos.getContent({
|
||||
owner: repoOwner,
|
||||
repo: repoName,
|
||||
path: "stirling-pdf/src/main/resources/messages_en_GB.properties",
|
||||
path: "src/main/resources/messages_en_GB.properties",
|
||||
ref: "main",
|
||||
});
|
||||
|
||||
@ -221,7 +204,6 @@ jobs:
|
||||
if: env.SCRIPT_OUTPUT != ''
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
github-token: ${{ steps.setup-bot.outputs.token }}
|
||||
script: |
|
||||
const { GITHUB_REPOSITORY, SCRIPT_OUTPUT } = process.env;
|
||||
const [repoOwner, repoName] = GITHUB_REPOSITORY.split('/');
|
||||
@ -237,7 +219,7 @@ jobs:
|
||||
const comment = comments.data.find(c => c.body.includes("## 🚀 Translation Verification Summary"));
|
||||
|
||||
// Only update or create comments by the action user
|
||||
const expectedActor = "${{ steps.setup-bot.outputs.app-slug }}[bot]";
|
||||
const expectedActor = "github-actions[bot]";
|
||||
|
||||
if (comment && comment.user.login === expectedActor) {
|
||||
// Update existing comment
|
||||
|
48
.github/workflows/licenses-update.yml
vendored
48
.github/workflows/licenses-update.yml
vendored
@ -16,52 +16,54 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
repository-projects: write # Required for enabling automerge
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup GitHub App Bot
|
||||
id: setup-bot
|
||||
uses: ./.github/actions/setup-bot
|
||||
- name: Generate GitHub App Token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "adopt"
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
|
||||
- uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
|
||||
|
||||
- name: Check licenses for compatibility
|
||||
- name: check the licenses for compatibility
|
||||
run: ./gradlew clean checkLicense
|
||||
|
||||
- name: Upload artifact on failure
|
||||
- name: FAILED - check the licenses for compatibility
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: dependencies-without-allowed-license.json
|
||||
path: build/reports/dependency-license/dependencies-without-allowed-license.json
|
||||
path: |
|
||||
build/reports/dependency-license/dependencies-without-allowed-license.json
|
||||
retention-days: 3
|
||||
|
||||
- name: Move and rename license file
|
||||
- name: Move and Rename License File
|
||||
run: |
|
||||
mv build/reports/dependency-license/index.json stirling-pdf/src/main/resources/static/3rdPartyLicenses.json
|
||||
mv build/reports/dependency-license/index.json src/main/resources/static/3rdPartyLicenses.json
|
||||
|
||||
- name: Commit changes
|
||||
- name: Set up git config
|
||||
run: |
|
||||
git add stirling-pdf/src/main/resources/static/3rdPartyLicenses.json
|
||||
git config --global user.name "stirlingbot[bot]"
|
||||
git config --global user.email "1113334+stirlingbot[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Run git add
|
||||
run: |
|
||||
git add src/main/resources/static/3rdPartyLicenses.json
|
||||
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
|
||||
|
||||
- name: Create Pull Request
|
||||
@ -69,15 +71,15 @@ jobs:
|
||||
if: env.CHANGES_DETECTED == 'true'
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
token: ${{ steps.setup-bot.outputs.token }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
commit-message: "Update 3rd Party Licenses"
|
||||
committer: ${{ steps.setup-bot.outputs.committer }}
|
||||
author: ${{ steps.setup-bot.outputs.committer }}
|
||||
committer: "stirlingbot[bot] <1113334+stirlingbot[bot]@users.noreply.github.com>"
|
||||
author: "stirlingbot[bot] <1113334+stirlingbot[bot]@users.noreply.github.com>"
|
||||
signoff: true
|
||||
branch: update-3rd-party-licenses
|
||||
title: "Update 3rd Party Licenses"
|
||||
body: |
|
||||
Auto-generated by ${{ steps.setup-bot.outputs.app-slug }}[bot]
|
||||
Auto-generated by StirlingBot
|
||||
labels: licenses,github-actions
|
||||
draft: false
|
||||
delete-branch: true
|
||||
@ -87,4 +89,4 @@ jobs:
|
||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||
run: gh pr merge --squash --auto "${{ steps.cpr.outputs.pull-request-number }}"
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.setup-bot.outputs.token }}
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
|
18
.github/workflows/multiOSReleases.yml
vendored
18
.github/workflows/multiOSReleases.yml
vendored
@ -48,11 +48,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
disable_security: [true, false]
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- disable_security: false
|
||||
- enable_security: true
|
||||
file_suffix: "-with-login"
|
||||
- disable_security: true
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
@ -72,10 +72,10 @@ jobs:
|
||||
with:
|
||||
gradle-version: 8.14
|
||||
|
||||
- name: Generate jar (Disable Security=${{ matrix.disable_security }})
|
||||
- name: Generate jar (With Security=${{ matrix.enable_security }})
|
||||
run: ./gradlew clean createExe
|
||||
env:
|
||||
DISABLE_ADDITIONAL_FEATURES: ${{ matrix.disable_security }}
|
||||
DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }}
|
||||
STIRLING_PDF_DESKTOP_UI: false
|
||||
|
||||
- name: Rename binaries
|
||||
@ -98,11 +98,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
disable_security: [true, false]
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- disable_security: false
|
||||
- enable_security: true
|
||||
file_suffix: "with-login-"
|
||||
- disable_security: true
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
@ -171,7 +171,7 @@ jobs:
|
||||
- name: Build Installer
|
||||
run: ./gradlew build jpackage -x test --info
|
||||
env:
|
||||
DISABLE_ADDITIONAL_FEATURES: true
|
||||
DOCKER_ENABLE_SECURITY: false
|
||||
STIRLING_PDF_DESKTOP_UI: true
|
||||
BROWSER_OPEN: true
|
||||
|
||||
|
43
.github/workflows/pre_commit.yml
vendored
43
.github/workflows/pre_commit.yml
vendored
@ -20,49 +20,58 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup GitHub App Bot
|
||||
id: setup-bot
|
||||
uses: ./.github/actions/setup-bot
|
||||
- name: Generate GitHub App Token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Get GitHub App User ID
|
||||
id: get-user-id
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.generate-token.outputs.app-slug }}[bot]" --jq .id)" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
- id: committer
|
||||
run: |
|
||||
echo "string=${{ steps.generate-token.outputs.app-slug }}[bot] <${{ steps.get-user-id.outputs.user-id }}+${{ steps.generate-token.outputs.app-slug }}[bot]@users.noreply.github.com>" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: 3.12
|
||||
cache: 'pip' # caching pip dependencies
|
||||
|
||||
- name: Run Pre-Commit Hooks
|
||||
run: |
|
||||
pip install --require-hashes -r ./.github/scripts/requirements_pre_commit.txt
|
||||
|
||||
- run: pre-commit run --all-files -c .pre-commit-config.yaml
|
||||
continue-on-error: true
|
||||
|
||||
- name: Set up git config
|
||||
run: |
|
||||
git config --global user.name ${{ steps.generate-token.outputs.app-slug }}[bot]
|
||||
git config --global user.email "${{ steps.get-user-id.outputs.user-id }}+${{ steps.generate-token.outputs.app-slug }}[bot]@users.noreply.github.com"
|
||||
- name: git add
|
||||
run: |
|
||||
git add .
|
||||
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
|
||||
|
||||
- name: Create Pull Request
|
||||
if: env.CHANGES_DETECTED == 'true'
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
token: ${{ steps.setup-bot.outputs.token }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
commit-message: ":file_folder: pre-commit"
|
||||
committer: ${{ steps.setup-bot.outputs.committer }}
|
||||
author: ${{ steps.setup-bot.outputs.committer }}
|
||||
committer: ${{ steps.committer.outputs.string }}
|
||||
author: ${{ steps.committer.outputs.string }}
|
||||
signoff: true
|
||||
branch: pre-commit
|
||||
title: "🤖 format everything with pre-commit by ${{ steps.setup-bot.outputs.app-slug }}"
|
||||
title: "🤖 format everything with pre-commit by <${{ steps.generate-token.outputs.app-slug }}>"
|
||||
body: |
|
||||
Auto-generated by [create-pull-request][1] with **${{ steps.setup-bot.outputs.app-slug }}**
|
||||
Auto-generated by [create-pull-request][1] with **${{ steps.generate-token.outputs.app-slug }}**
|
||||
|
||||
[1]: https://github.com/peter-evans/create-pull-request
|
||||
draft: false
|
||||
|
8
.github/workflows/push-docker.yml
vendored
8
.github/workflows/push-docker.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
- name: Run Gradle Command
|
||||
run: ./gradlew clean build
|
||||
env:
|
||||
DISABLE_ADDITIONAL_FEATURES: true
|
||||
DOCKER_ENABLE_SECURITY: false
|
||||
STIRLING_PDF_DESKTOP_UI: false
|
||||
|
||||
- name: Install cosign
|
||||
@ -90,7 +90,7 @@ jobs:
|
||||
|
||||
- name: Build and push main Dockerfile
|
||||
id: build-push-regular
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
context: .
|
||||
@ -135,7 +135,7 @@ jobs:
|
||||
|
||||
- name: Build and push Dockerfile-ultra-lite
|
||||
id: build-push-lite
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
|
||||
if: github.ref != 'refs/heads/main'
|
||||
with:
|
||||
context: .
|
||||
@ -166,7 +166,7 @@ jobs:
|
||||
|
||||
- name: Build and push main Dockerfile fat
|
||||
id: build-push-fat
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
|
||||
if: github.ref != 'refs/heads/main'
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
|
22
.github/workflows/releaseArtifacts.yml
vendored
22
.github/workflows/releaseArtifacts.yml
vendored
@ -13,11 +13,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
disable_security: [true, false]
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- disable_security: false
|
||||
- enable_security: true
|
||||
file_suffix: "-with-login"
|
||||
- disable_security: true
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
outputs:
|
||||
version: ${{ steps.versionNumber.outputs.versionNumber }}
|
||||
@ -39,10 +39,10 @@ jobs:
|
||||
with:
|
||||
gradle-version: 8.14
|
||||
|
||||
- name: Generate jar (Disable Security=${{ matrix.disable_security }})
|
||||
- name: Generate jar (With Security=${{ matrix.enable_security }})
|
||||
run: ./gradlew clean createExe
|
||||
env:
|
||||
DISABLE_ADDITIONAL_FEATURES: ${{ matrix.disable_security }}
|
||||
DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }}
|
||||
STIRLING_PDF_DESKTOP_UI: false
|
||||
|
||||
- name: Get version number
|
||||
@ -75,11 +75,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
disable_security: [true, false]
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- disable_security: false
|
||||
- enable_security: true
|
||||
file_suffix: "-with-login"
|
||||
- disable_security: true
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
@ -153,11 +153,11 @@ jobs:
|
||||
contents: write
|
||||
strategy:
|
||||
matrix:
|
||||
disable_security: [true, false]
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- disable_security: false
|
||||
- enable_security: true
|
||||
file_suffix: "-with-login"
|
||||
- disable_security: true
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
|
2
.github/workflows/scorecards.yml
vendored
2
.github/workflows/scorecards.yml
vendored
@ -44,7 +44,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
|
||||
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
|
2
.github/workflows/sonarqube.yml
vendored
2
.github/workflows/sonarqube.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
DISABLE_ADDITIONAL_FEATURES: false
|
||||
DOCKER_ENABLE_SECURITY: true
|
||||
STIRLING_PDF_DESKTOP_UI: true
|
||||
run: |
|
||||
./gradlew clean build sonar \
|
||||
|
79
.github/workflows/sync_files.yml
vendored
79
.github/workflows/sync_files.yml
vendored
@ -8,15 +8,52 @@ on:
|
||||
paths:
|
||||
- "build.gradle"
|
||||
- "README.md"
|
||||
- "stirling-pdf/src/main/resources/messages_*.properties"
|
||||
- "stirling-pdf/src/main/resources/static/3rdPartyLicenses.json"
|
||||
- "src/main/resources/messages_*.properties"
|
||||
- "src/main/resources/static/3rdPartyLicenses.json"
|
||||
- "scripts/ignore_translation.toml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
read_bot_entries:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
userName: ${{ steps.get-user-id.outputs.user_name }}
|
||||
userEmail: ${{ steps.get-user-id.outputs.user_email }}
|
||||
committer: ${{ steps.committer.outputs.committer }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Generate GitHub App Token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Get GitHub App User ID
|
||||
id: get-user-id
|
||||
run: |
|
||||
USER_NAME="${{ steps.generate-token.outputs.app-slug }}[bot]"
|
||||
USER_ID=$(gh api "/users/$USER_NAME" --jq .id)
|
||||
USER_EMAIL="$USER_ID+$USER_NAME@users.noreply.github.com"
|
||||
echo "user_name=$USER_NAME" >> "$GITHUB_OUTPUT"
|
||||
echo "user_email=$USER_EMAIL" >> "$GITHUB_OUTPUT"
|
||||
echo "user-id=$USER_ID" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
- id: committer
|
||||
run: |
|
||||
COMMITTER="${{ steps.get-user-id.outputs.user_name }} <${{ steps.get-user-id.outputs.user_email }}>"
|
||||
echo "committer=$COMMITTER" >> "$GITHUB_OUTPUT"
|
||||
|
||||
sync-files:
|
||||
needs: ["read_bot_entries"]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
@ -24,29 +61,34 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Setup GitHub App Bot
|
||||
id: setup-bot
|
||||
uses: ./.github/actions/setup-bot
|
||||
- name: Generate GitHub App Token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
app-id: ${{ vars.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: "pip" # caching pip dependencies
|
||||
cache: 'pip' # caching pip dependencies
|
||||
|
||||
- name: Sync translation property files
|
||||
run: |
|
||||
python .github/scripts/check_language_properties.py --reference-file "stirling-pdf/src/main/resources/messages_en_GB.properties" --branch main
|
||||
python .github/scripts/check_language_properties.py --reference-file "src/main/resources/messages_en_GB.properties" --branch main
|
||||
|
||||
- name: Commit translation files
|
||||
- name: Set up git config
|
||||
run: |
|
||||
git add stirling-pdf/src/main/resources/messages_*.properties
|
||||
git diff --staged --quiet || git commit -m ":memo: Sync translation files" || echo "No changes detected"
|
||||
git config --global user.name ${{ needs.read_bot_entries.outputs.userName }}
|
||||
git config --global user.email ${{ needs.read_bot_entries.outputs.userEmail }}
|
||||
|
||||
- name: Run git add
|
||||
run: |
|
||||
git add src/main/resources/messages_*.properties
|
||||
git diff --staged --quiet || git commit -m ":memo: Sync translation files" || echo "no changes"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install --require-hashes -r ./.github/scripts/requirements_sync_readme.txt
|
||||
@ -58,16 +100,15 @@ jobs:
|
||||
- name: Run git add
|
||||
run: |
|
||||
git add README.md
|
||||
git diff --staged --quiet || git commit -m ":memo: Sync README.md" || echo "No changes detected"
|
||||
git diff --staged --quiet || git commit -m ":memo: Sync README.md" || echo "no changes"
|
||||
|
||||
- name: Create Pull Request
|
||||
if: always()
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
token: ${{ steps.setup-bot.outputs.token }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
commit-message: Update files
|
||||
committer: ${{ steps.setup-bot.outputs.committer }}
|
||||
author: ${{ steps.setup-bot.outputs.committer }}
|
||||
committer: ${{ needs.read_bot_entries.outputs.committer }}
|
||||
author: ${{ needs.read_bot_entries.outputs.committer }}
|
||||
signoff: true
|
||||
branch: sync_readme
|
||||
title: ":globe_with_meridians: Sync Translations + Update README Progress Table"
|
||||
@ -101,4 +142,4 @@ jobs:
|
||||
sign-commits: true
|
||||
add-paths: |
|
||||
README.md
|
||||
stirling-pdf/src/main/resources/messages_*.properties
|
||||
src/main/resources/messages_*.properties
|
||||
|
6
.github/workflows/testdriver.yml
vendored
6
.github/workflows/testdriver.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean build
|
||||
env:
|
||||
DISABLE_ADDITIONAL_FEATURES: true
|
||||
DOCKER_ENABLE_SECURITY: false
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
@ -46,7 +46,7 @@ jobs:
|
||||
password: ${{ secrets.DOCKER_HUB_API }}
|
||||
|
||||
- name: Build and push test image
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
@ -76,7 +76,7 @@ jobs:
|
||||
- /stirling/test-${{ github.sha }}/config:/configs:rw
|
||||
- /stirling/test-${{ github.sha }}/logs:/logs:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "true"
|
||||
DOCKER_ENABLE_SECURITY: "false"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
SYSTEM_DEFAULTLOCALE: en-GB
|
||||
UI_APPNAME: "Stirling-PDF Test"
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -13,7 +13,6 @@ local.properties
|
||||
.recommenders
|
||||
.classpath
|
||||
.project
|
||||
*.local.json
|
||||
version.properties
|
||||
|
||||
#### Stirling-PDF Files ###
|
||||
@ -125,9 +124,6 @@ SwaggerDoc.json
|
||||
*.rar
|
||||
*.db
|
||||
/build
|
||||
/stirling-pdf/build
|
||||
/common/build
|
||||
/proprietary/build
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
@ -197,3 +193,4 @@ id_ed25519.pub
|
||||
|
||||
# node_modules
|
||||
node_modules/
|
||||
*.mjs
|
||||
|
@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.11.11
|
||||
rev: v0.11.6
|
||||
hooks:
|
||||
- id: ruff
|
||||
args:
|
||||
@ -20,9 +20,9 @@ repos:
|
||||
- --skip="./.*,*.csv,*.json,*.ambr"
|
||||
- --quiet-level=2
|
||||
files: \.(html|css|js|py|md)$
|
||||
exclude: (.vscode|.devcontainer|stirling-pdf/src/main/resources|Dockerfile|.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js)
|
||||
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile|.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js)
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.26.0
|
||||
rev: v8.24.3
|
||||
hooks:
|
||||
- id: gitleaks
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
|
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@ -10,7 +10,7 @@
|
||||
"java.configuration.updateBuildConfiguration": "interactive",
|
||||
"java.format.enabled": true,
|
||||
"java.format.settings.profile": "GoogleStyle",
|
||||
"java.format.settings.google.version": "1.27.0",
|
||||
"java.format.settings.google.version": "1.26.0",
|
||||
"java.format.settings.google.extra": "--aosp --skip-sorting-imports --skip-javadoc-formatting",
|
||||
// (DE) Aktiviert Kommentare im Java-Format.
|
||||
// (EN) Enables comments in Java formatting.
|
||||
@ -49,11 +49,7 @@
|
||||
".venv*/",
|
||||
".vscode/",
|
||||
"bin/",
|
||||
"common/bin/",
|
||||
"proprietary/bin/",
|
||||
"build/",
|
||||
"common/build/",
|
||||
"proprietary/build/",
|
||||
"configs/",
|
||||
"customFiles/",
|
||||
"docs/",
|
||||
@ -67,8 +63,6 @@
|
||||
".git-blame-ignore-revs",
|
||||
".gitattributes",
|
||||
".gitignore",
|
||||
"common/.gitignore",
|
||||
"proprietary/.gitignore",
|
||||
".pre-commit-config.yaml",
|
||||
],
|
||||
// Enables signature help in Java.
|
||||
|
@ -55,7 +55,7 @@ Stirling-PDF uses Lombok to reduce boilerplate code. Some IDEs, like Eclipse, do
|
||||
Visit the [Lombok website](https://projectlombok.org/setup/) for installation instructions specific to your IDE.
|
||||
|
||||
5. Add environment variable
|
||||
For local testing, you should generally be testing the full 'Security' version of Stirling PDF. To do this, you must add the environment flag DISABLE_ADDITIONAL_FEATURES=false to your system and/or IDE build/run step.
|
||||
For local testing, you should generally be testing the full 'Security' version of Stirling-PDF. To do this, you must add the environment flag DOCKER_ENABLE_SECURITY=true to your system and/or IDE build/run step.
|
||||
|
||||
## 4. Project Structure
|
||||
|
||||
@ -114,9 +114,9 @@ Stirling-PDF offers several Docker versions:
|
||||
|
||||
Stirling-PDF provides several example Docker Compose files in the `exampleYmlFiles` directory, such as:
|
||||
|
||||
- `docker-compose-latest.yml`: Latest version without login and security features
|
||||
- `docker-compose-latest-security.yml`: Latest version with login and security features enabled
|
||||
- `docker-compose-latest-fat-security.yml`: Fat version with login and security features enabled
|
||||
- `docker-compose-latest.yml`: Latest version without security features
|
||||
- `docker-compose-latest-security.yml`: Latest version with security features enabled
|
||||
- `docker-compose-latest-fat-security.yml`: Fat version with security features enabled
|
||||
|
||||
These files provide pre-configured setups for different scenarios. For example, here's a snippet from `docker-compose-latest-security.yml`:
|
||||
|
||||
@ -137,11 +137,11 @@ 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"
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
PUID: 1002
|
||||
PGID: 1002
|
||||
@ -170,7 +170,7 @@ Stirling-PDF uses different Docker images for various configurations. The build
|
||||
1. Set the security environment variable:
|
||||
|
||||
```bash
|
||||
export DISABLE_ADDITIONAL_FEATURES=true # or false for to enable login and security features for builds
|
||||
export DOCKER_ENABLE_SECURITY=false # or true for security-enabled builds
|
||||
```
|
||||
|
||||
2. Build the project with Gradle:
|
||||
@ -193,10 +193,10 @@ Stirling-PDF uses different Docker images for various configurations. The build
|
||||
docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite .
|
||||
```
|
||||
|
||||
For the fat version (with login and security features enabled):
|
||||
For the fat version (with security enabled):
|
||||
|
||||
```bash
|
||||
export DISABLE_ADDITIONAL_FEATURES=false
|
||||
export DOCKER_ENABLE_SECURITY=true
|
||||
docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-fat -f ./Dockerfile.fat .
|
||||
```
|
||||
|
||||
@ -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 `stirling-pdf/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 `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 `stirling-pdf/src/main/java/stirling/software/SPDF/controller/api` directory.
|
||||
- Create a new Java class in the `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 `stirling-pdf/src/main/java/stirling/software/SPDF/service` directory.
|
||||
- Create a new service class in the `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 `stirling-pdf/src/main/resources/templates` directory.
|
||||
- Create a new HTML file in the `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 `stirling-pdf/src/main/java/stirling/software/SPDF/controller/ui` directory.
|
||||
- Create a new Java class in the `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 `stirling-pdf/src/main/resources/templates/fragments/navbar.html` file.
|
||||
- Update the `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 `stirling-pdf/src/main/resources` directory. You'll see files like:
|
||||
Find the existing `messages.properties` files in the `src/main/resources` directory. You'll see files like:
|
||||
|
||||
- `messages.properties` (default, usually English)
|
||||
- `messages_en_GB.properties`
|
||||
|
11
Dockerfile
11
Dockerfile
@ -1,11 +1,12 @@
|
||||
# Main stage
|
||||
FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be023728e11715
|
||||
FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
|
||||
|
||||
# Copy necessary files
|
||||
COPY scripts /scripts
|
||||
COPY pipeline /pipeline
|
||||
COPY stirling-pdf/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||
COPY stirling-pdf/build/libs/*.jar app.jar
|
||||
COPY 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
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
||||
@ -22,7 +23,7 @@ LABEL org.opencontainers.image.version="${VERSION_TAG}"
|
||||
LABEL org.opencontainers.image.keywords="PDF, manipulation, merge, split, convert, OCR, watermark"
|
||||
|
||||
# Set Environment Variables
|
||||
ENV DISABLE_ADDITIONAL_FEATURES=true \
|
||||
ENV DOCKER_ENABLE_SECURITY=false \
|
||||
VERSION_TAG=$VERSION_TAG \
|
||||
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||
JAVA_CUSTOM_OPTS="" \
|
||||
@ -72,7 +73,7 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
|
||||
py3-pillow@testing \
|
||||
py3-pdf2image@testing && \
|
||||
python3 -m venv /opt/venv && \
|
||||
/opt/venv/bin/pip install --upgrade pip setuptools && \
|
||||
/opt/venv/bin/pip install --upgrade pip && \
|
||||
/opt/venv/bin/pip install --no-cache-dir --upgrade unoserver weasyprint && \
|
||||
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||
|
@ -32,7 +32,6 @@ ENV SETUPTOOLS_USE_DISTUTILS=local
|
||||
# Installation der benötigten Python-Pakete
|
||||
RUN python3 -m venv --system-site-packages /opt/venv \
|
||||
&& . /opt/venv/bin/activate \
|
||||
&& pip install --upgrade pip setuptools \
|
||||
&& pip install --no-cache-dir WeasyPrint pdf2image pillow unoserver opencv-python-headless pre-commit
|
||||
|
||||
# Füge den venv-Pfad zur globalen PATH-Variable hinzu, damit die Tools verfügbar sind
|
||||
|
@ -5,9 +5,6 @@ COPY build.gradle .
|
||||
COPY settings.gradle .
|
||||
COPY gradlew .
|
||||
COPY gradle gradle/
|
||||
COPY stirling-pdf/build.gradle stirling-pdf/.
|
||||
COPY common/build.gradle common/.
|
||||
COPY proprietary/build.gradle proprietary/.
|
||||
RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0
|
||||
|
||||
# Set the working directory
|
||||
@ -16,24 +13,24 @@ WORKDIR /app
|
||||
# Copy the entire project to the working directory
|
||||
COPY . .
|
||||
|
||||
# Build the application with DISABLE_ADDITIONAL_FEATURES=false
|
||||
RUN DISABLE_ADDITIONAL_FEATURES=false \
|
||||
# Build the application with DOCKER_ENABLE_SECURITY=false
|
||||
RUN DOCKER_ENABLE_SECURITY=true \
|
||||
STIRLING_PDF_DESKTOP_UI=false \
|
||||
./gradlew clean build -x spotlessApply -x spotlessCheck -x test -x sonarqube
|
||||
|
||||
# Main stage
|
||||
FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be023728e11715
|
||||
FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
|
||||
|
||||
# Copy necessary files
|
||||
COPY scripts /scripts
|
||||
COPY pipeline /pipeline
|
||||
COPY stirling-pdf/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||
COPY --from=build /app/stirling-pdf/build/libs/*.jar app.jar
|
||||
COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||
COPY --from=build /app/build/libs/*.jar app.jar
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
||||
# Set Environment Variables
|
||||
ENV DISABLE_ADDITIONAL_FEATURES=true \
|
||||
ENV DOCKER_ENABLE_SECURITY=false \
|
||||
VERSION_TAG=$VERSION_TAG \
|
||||
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||
JAVA_CUSTOM_OPTS="" \
|
||||
@ -86,7 +83,7 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
|
||||
py3-pillow@testing \
|
||||
py3-pdf2image@testing && \
|
||||
python3 -m venv /opt/venv && \
|
||||
/opt/venv/bin/pip install --upgrade pip setuptools && \
|
||||
/opt/venv/bin/pip install --upgrade pip && \
|
||||
/opt/venv/bin/pip install --no-cache-dir --upgrade unoserver weasyprint && \
|
||||
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||
|
@ -1,10 +1,10 @@
|
||||
# use alpine
|
||||
FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be023728e11715
|
||||
FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
||||
# Set Environment Variables
|
||||
ENV DISABLE_ADDITIONAL_FEATURES=true \
|
||||
ENV DOCKER_ENABLE_SECURITY=false \
|
||||
HOME=/home/stirlingpdfuser \
|
||||
VERSION_TAG=$VERSION_TAG \
|
||||
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||
@ -18,7 +18,7 @@ COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
||||
COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||
COPY scripts/installFonts.sh /scripts/installFonts.sh
|
||||
COPY pipeline /pipeline
|
||||
COPY stirling-pdf/build/libs/*.jar app.jar
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
# Set up necessary directories and permissions
|
||||
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||
|
@ -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/stirling-pdf/src/main/resources/templates/fragments/languages.html)
|
||||
- Edit the file: [languages.html](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/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/stirling-pdf/src/main/resources/messages_en_GB.properties)
|
||||
- [messages_en_GB.properties](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/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`.
|
||||
|
||||
|
9
LICENSE
9
LICENSE
@ -1,13 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Stirling PDF Inc.
|
||||
|
||||
Portions of this software are licensed as follows:
|
||||
|
||||
* All content that resides under the "proprietary/" directory of this repository,
|
||||
if that directory exists, is licensed under the license defined in "proprietary/LICENSE".
|
||||
* Content outside of the above mentioned directories or restrictions above is
|
||||
available under the MIT License as defined below.
|
||||
Copyright (c) 2024 Stirling Tools
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
78
README.md
78
README.md
@ -116,47 +116,47 @@ Stirling-PDF currently supports 40 languages!
|
||||
|
||||
| Language | Progress |
|
||||
| -------------------------------------------- | -------------------------------------- |
|
||||
| Arabic (العربية) (ar_AR) |  |
|
||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||
| Basque (Euskara) (eu_ES) |  |
|
||||
| Bulgarian (Български) (bg_BG) |  |
|
||||
| Catalan (Català) (ca_CA) |  |
|
||||
| Croatian (Hrvatski) (hr_HR) |  |
|
||||
| Czech (Česky) (cs_CZ) |  |
|
||||
| Danish (Dansk) (da_DK) |  |
|
||||
| Dutch (Nederlands) (nl_NL) |  |
|
||||
| Arabic (العربية) (ar_AR) |  |
|
||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||
| Basque (Euskara) (eu_ES) |  |
|
||||
| Bulgarian (Български) (bg_BG) |  |
|
||||
| Catalan (Català) (ca_CA) |  |
|
||||
| Croatian (Hrvatski) (hr_HR) |  |
|
||||
| Czech (Česky) (cs_CZ) |  |
|
||||
| Danish (Dansk) (da_DK) |  |
|
||||
| Dutch (Nederlands) (nl_NL) |  |
|
||||
| English (English) (en_GB) |  |
|
||||
| English (US) (en_US) |  |
|
||||
| French (Français) (fr_FR) |  |
|
||||
| German (Deutsch) (de_DE) |  |
|
||||
| Greek (Ελληνικά) (el_GR) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||
| Irish (Gaeilge) (ga_IE) |  |
|
||||
| Italian (Italiano) (it_IT) |  |
|
||||
| Japanese (日本語) (ja_JP) |  |
|
||||
| Korean (한국어) (ko_KR) |  |
|
||||
| Norwegian (Norsk) (no_NB) |  |
|
||||
| Persian (فارسی) (fa_IR) |  |
|
||||
| Polish (Polski) (pl_PL) |  |
|
||||
| Portuguese (Português) (pt_PT) |  |
|
||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||
| Romanian (Română) (ro_RO) |  |
|
||||
| Russian (Русский) (ru_RU) |  |
|
||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||
| Slovakian (Slovensky) (sk_SK) |  |
|
||||
| Slovenian (Slovenščina) (sl_SI) |  |
|
||||
| Spanish (Español) (es_ES) |  |
|
||||
| Swedish (Svenska) (sv_SE) |  |
|
||||
| Thai (ไทย) (th_TH) |  |
|
||||
| Tibetan (བོད་ཡིག་) (bo_CN) |  |
|
||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||
| Turkish (Türkçe) (tr_TR) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||
| Malayalam (മലയാളം) (ml_IN) |  |
|
||||
| French (Français) (fr_FR) |  |
|
||||
| German (Deutsch) (de_DE) |  |
|
||||
| Greek (Ελληνικά) (el_GR) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||
| Irish (Gaeilge) (ga_IE) |  |
|
||||
| Italian (Italiano) (it_IT) |  |
|
||||
| Japanese (日本語) (ja_JP) |  |
|
||||
| Korean (한국어) (ko_KR) |  |
|
||||
| Norwegian (Norsk) (no_NB) |  |
|
||||
| Persian (فارسی) (fa_IR) |  |
|
||||
| Polish (Polski) (pl_PL) |  |
|
||||
| Portuguese (Português) (pt_PT) |  |
|
||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||
| Romanian (Română) (ro_RO) |  |
|
||||
| Russian (Русский) (ru_RU) |  |
|
||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||
| Slovakian (Slovensky) (sk_SK) |  |
|
||||
| Slovenian (Slovenščina) (sl_SI) |  |
|
||||
| Spanish (Español) (es_ES) |  |
|
||||
| Swedish (Svenska) (sv_SE) |  |
|
||||
| Thai (ไทย) (th_TH) |  |
|
||||
| Tibetan (བོད་ཡིག་) (zh_BO) |  |
|
||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||
| Turkish (Türkçe) (tr_TR) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||
| Malayalam (മലയാളം) (ml_ML) |  |
|
||||
|
||||
## Stirling PDF Enterprise
|
||||
|
||||
|
460
build.gradle
460
build.gradle
@ -1,15 +1,15 @@
|
||||
plugins {
|
||||
id "java"
|
||||
id "jacoco"
|
||||
id 'jacoco'
|
||||
id "org.springframework.boot" version "3.4.5"
|
||||
id "io.spring.dependency-management" version "1.1.7"
|
||||
id "org.springframework.boot" version "3.5.0"
|
||||
id "org.springdoc.openapi-gradle-plugin" version "1.9.0"
|
||||
id "io.swagger.swaggerhub" version "1.3.2"
|
||||
id "edu.sc.seis.launch4j" version "3.0.6"
|
||||
id "com.diffplug.spotless" version "7.0.4"
|
||||
id "com.diffplug.spotless" version "7.0.3"
|
||||
id "com.github.jk1.dependency-license-report" version "2.9"
|
||||
//id "nebula.lint" version "19.0.3"
|
||||
id "org.panteleyev.jpackageplugin" version "1.6.1"
|
||||
id("org.panteleyev.jpackageplugin") version "1.6.1"
|
||||
id "org.sonarqube" version "6.2.0.5505"
|
||||
}
|
||||
|
||||
@ -19,136 +19,28 @@ import java.nio.file.Files
|
||||
import java.time.Year
|
||||
|
||||
ext {
|
||||
springBootVersion = "3.5.0"
|
||||
springBootVersion = "3.4.5"
|
||||
pdfboxVersion = "3.0.5"
|
||||
imageioVersion = "3.12.0"
|
||||
lombokVersion = "1.18.38"
|
||||
bouncycastleVersion = "1.80"
|
||||
springSecuritySamlVersion = "6.5.0"
|
||||
openSamlVersion = "4.3.2"
|
||||
commonmarkVersion = "0.24.0"
|
||||
tempJrePath = null
|
||||
}
|
||||
|
||||
jar {
|
||||
enabled = false
|
||||
manifest {
|
||||
attributes "Implementation-Title": "Stirling-PDF",
|
||||
"Implementation-Version": project.version
|
||||
}
|
||||
group = "stirling.software"
|
||||
version = "0.46.2"
|
||||
|
||||
java {
|
||||
// 17 is lowest but we support and recommend 21
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
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 = '1.0.0'
|
||||
|
||||
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'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.12.2'
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = "UTF-8"
|
||||
dependsOn "spotlessApply"
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.compilerArgs << "-parameters"
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = "UTF-8"
|
||||
dependsOn "spotlessApply"
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url = "https://build.shibboleth.net/maven/releases" }
|
||||
maven { url = "https://maven.pkg.github.com/jcefmaven/jcefmaven" }
|
||||
}
|
||||
|
||||
licenseReport {
|
||||
@ -159,14 +51,29 @@ licenseReport {
|
||||
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("DOCKER_ENABLE_SECURITY") == "false") {
|
||||
exclude "stirling/software/SPDF/config/interfaces/DatabaseInterface.java"
|
||||
exclude "stirling/software/SPDF/config/security/**"
|
||||
exclude "stirling/software/SPDF/controller/api/DatabaseController.java"
|
||||
exclude "stirling/software/SPDF/controller/api/EmailController.java"
|
||||
exclude "stirling/software/SPDF/controller/api/H2SQLCondition.java"
|
||||
exclude "stirling/software/SPDF/controller/api/UserController.java"
|
||||
exclude "stirling/software/SPDF/controller/web/AccountWebController.java"
|
||||
exclude "stirling/software/SPDF/controller/web/DatabaseWebController.java"
|
||||
exclude "stirling/software/SPDF/model/api/Email.java"
|
||||
exclude "stirling/software/SPDF/model/ApiKeyAuthenticationToken.java"
|
||||
exclude "stirling/software/SPDF/model/AttemptCounter.java"
|
||||
exclude "stirling/software/SPDF/model/Authority.java"
|
||||
exclude "stirling/software/SPDF/model/exception/BackupNotFoundException.java"
|
||||
exclude "stirling/software/SPDF/model/exception/NoProviderFoundException.java"
|
||||
exclude "stirling/software/SPDF/model/PersistentLogin.java"
|
||||
exclude "stirling/software/SPDF/model/SessionEntity.java"
|
||||
exclude "stirling/software/SPDF/model/User.java"
|
||||
exclude "stirling/software/SPDF/repository/**"
|
||||
}
|
||||
|
||||
if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') {
|
||||
exclude 'stirling/software/SPDF/UI/impl/**'
|
||||
if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") {
|
||||
exclude "stirling/software/SPDF/UI/impl/**"
|
||||
}
|
||||
|
||||
}
|
||||
@ -174,14 +81,15 @@ sourceSets {
|
||||
|
||||
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("DOCKER_ENABLE_SECURITY") == "false") {
|
||||
exclude "stirling/software/SPDF/config/security/**"
|
||||
exclude "stirling/software/SPDF/model/ApiKeyAuthenticationTokenTest.java"
|
||||
exclude "stirling/software/SPDF/controller/api/EmailControllerTest.java"
|
||||
exclude "stirling/software/SPDF/repository/**"
|
||||
}
|
||||
|
||||
if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') {
|
||||
exclude 'stirling/software/SPDF/UI/impl/**'
|
||||
if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") {
|
||||
exclude "stirling/software/SPDF/UI/impl/**"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,9 +115,10 @@ 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 = "stirling-pdf/src/main/resources/static/favicon.ico"
|
||||
icon = "src/main/resources/static/favicon.ico"
|
||||
verbose = true
|
||||
// mainClass = "org.springframework.boot.loader.launch.JarLauncher"
|
||||
|
||||
@ -217,7 +126,6 @@ jpackage {
|
||||
javaOptions = [
|
||||
"-DBROWSER_OPEN=true",
|
||||
"-DSTIRLING_PDF_DESKTOP_UI=true",
|
||||
"-DDISABLE_ADDITIONAL_FEATURES=false",
|
||||
"-Djava.awt.headless=false",
|
||||
"-Dapple.awt.UIElement=true",
|
||||
"--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||
@ -248,10 +156,10 @@ jpackage {
|
||||
installDir = "C:/Program Files/Stirling-PDF"
|
||||
}
|
||||
|
||||
// MacOS-specific configuration
|
||||
// macOS-specific configuration
|
||||
mac {
|
||||
appVersion = getMacVersion(project.version.toString())
|
||||
icon = "stirling-pdf/src/main/resources/static/favicon.icns"
|
||||
icon = "src/main/resources/static/favicon.icns"
|
||||
type = "dmg"
|
||||
macPackageIdentifier = "Stirling PDF"
|
||||
macPackageName = "Stirling PDF"
|
||||
@ -273,7 +181,7 @@ jpackage {
|
||||
// Linux-specific configuration
|
||||
linux {
|
||||
appVersion = project.version
|
||||
icon = "stirling-pdf/src/main/resources/static/favicon.png"
|
||||
icon = "src/main/resources/static/favicon.png"
|
||||
type = "deb" // Can also use "rpm" for Red Hat-based systems
|
||||
|
||||
// Debian package configuration
|
||||
@ -309,15 +217,10 @@ jpackage {
|
||||
]*/
|
||||
|
||||
// Add copyright and license information
|
||||
copyright = "Copyright © 2025 Stirling PDF Inc."
|
||||
copyright = "Copyright © 2024 Stirling Software"
|
||||
licenseFile = "LICENSE"
|
||||
}
|
||||
|
||||
//tasks.wrapper {
|
||||
// gradleVersion = "8.14"
|
||||
// distributionType = Wrapper.DistributionType.ALL
|
||||
//}
|
||||
|
||||
tasks.register('jpackageMacX64') {
|
||||
group = 'distribution'
|
||||
description = 'Packages app for MacOS x86_64'
|
||||
@ -350,7 +253,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', 'stirling-pdf/src/main/resources/static/favicon.icns',
|
||||
'--icon', '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)',
|
||||
@ -359,7 +262,6 @@ tasks.register('jpackageMacX64') {
|
||||
// Java options
|
||||
'--java-options', '-DBROWSER_OPEN=true',
|
||||
'--java-options', '-DSTIRLING_PDF_DESKTOP_UI=true',
|
||||
'--java-options', '-DDISABLE_ADDITIONAL_FEATURES=false',
|
||||
'--java-options', '-Djava.awt.headless=false',
|
||||
'--java-options', '-Dapple.awt.UIElement=true',
|
||||
'--java-options', '--add-opens=java.base/java.lang=ALL-UNNAMED',
|
||||
@ -388,6 +290,8 @@ tasks.register('jpackageMacX64') {
|
||||
}
|
||||
}
|
||||
|
||||
//jpackage.finalizedBy(jpackageMacX64)
|
||||
|
||||
tasks.register('downloadTempJre') {
|
||||
group = 'distribution'
|
||||
description = 'Downloads and extracts a temporary JRE'
|
||||
@ -399,18 +303,18 @@ 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'
|
||||
}.result.get()
|
||||
|
||||
println "JRE ready at: $jreDir"
|
||||
println "✅ JRE ready at: $jreDir"
|
||||
ext.tempJrePath = jreDir.absolutePath
|
||||
project.ext.tempJrePath = jreDir.absolutePath
|
||||
} catch (Exception e) {
|
||||
@ -436,7 +340,7 @@ tasks.register('cleanTempJre') {
|
||||
}
|
||||
|
||||
launch4j {
|
||||
icon = "${projectDir}/stirling-pdf/src/main/resources/static/favicon.ico"
|
||||
icon = "${projectDir}/src/main/resources/static/favicon.ico"
|
||||
|
||||
outfile="Stirling-PDF.exe"
|
||||
|
||||
@ -447,7 +351,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') {
|
||||
@ -470,12 +374,9 @@ launch4j {
|
||||
|
||||
spotless {
|
||||
java {
|
||||
target sourceSets.main.allJava
|
||||
target project(':common').sourceSets.main.allJava
|
||||
target project(':proprietary').sourceSets.main.allJava
|
||||
target project(':stirling-pdf').sourceSets.main.allJava
|
||||
target project.fileTree('src').include('**/*.java')
|
||||
|
||||
googleJavaFormat("1.27.0").aosp().reorderImports(false)
|
||||
googleJavaFormat("1.26.0").aosp().reorderImports(false)
|
||||
|
||||
importOrder("java", "javax", "org", "com", "net", "io", "jakarta", "lombok", "me", "stirling")
|
||||
toggleOffOn()
|
||||
@ -490,12 +391,190 @@ 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 {
|
||||
|
||||
//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:132.3.1"
|
||||
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'
|
||||
|
||||
|
||||
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") {
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
implementation 'io.micrometer:micrometer-registry-prometheus'
|
||||
|
||||
implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
|
||||
implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE"
|
||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
|
||||
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
|
||||
implementation "org.springframework.boot:spring-boot-starter-mail:$springBootVersion"
|
||||
|
||||
implementation "org.springframework.session:spring-session-core:3.4.3"
|
||||
implementation "org.springframework:spring-jdbc:6.2.7"
|
||||
|
||||
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
|
||||
// Don't upgrade h2database
|
||||
runtimeOnly "com.h2database:h2:2.3.232"
|
||||
runtimeOnly "org.postgresql:postgresql:42.7.5"
|
||||
constraints {
|
||||
implementation "org.opensaml:opensaml-core:$openSamlVersion"
|
||||
implementation "org.opensaml:opensaml-saml-api:$openSamlVersion"
|
||||
implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion"
|
||||
}
|
||||
implementation "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion"
|
||||
// implementation 'org.springframework.security:spring-security-core:$springSecuritySamlVersion'
|
||||
implementation 'com.coveo:saml-client:5.0.0'
|
||||
|
||||
}
|
||||
implementation 'org.snakeyaml:snakeyaml-engine:2.9'
|
||||
|
||||
testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
|
||||
// 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-hdr:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-icns:$imageioVersion"
|
||||
// runtimeOnly "com.twelvemonkeys.imageio:imageio-iff:$imageioVersion"
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-jpeg:$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-tiff:$imageioVersion"
|
||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-webp:$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"
|
||||
}
|
||||
|
||||
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.17.0'
|
||||
|
||||
|
||||
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
|
||||
}
|
||||
|
||||
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
|
||||
@ -506,49 +585,25 @@ swaggerhubUpload {
|
||||
oas = "3.0.0" // The version of the OpenAPI Specification you"re using
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.12.2'
|
||||
jar {
|
||||
enabled = false
|
||||
manifest {
|
||||
attributes "Implementation-Title": "Stirling-PDF",
|
||||
"Implementation-Version": project.version
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("test") {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.register('writeVersion') {
|
||||
def propsFile = file("$projectDir/common/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)
|
||||
project(':stirling-pdf').tasks.bootJar.dependsOn(writeVersion)
|
||||
|
||||
tasks.register('printVersion') {
|
||||
task printVersion {
|
||||
doLast {
|
||||
println project.version
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('printMacVersion') {
|
||||
task printMacVersion {
|
||||
doLast {
|
||||
println getMacVersion(project.version.toString())
|
||||
}
|
||||
@ -557,22 +612,3 @@ tasks.register('printMacVersion') {
|
||||
tasks.named('generateOpenApiDocs') {
|
||||
doNotTrackState("Tracking state is not supported for this task")
|
||||
}
|
||||
tasks.named('bootRun') {
|
||||
group = 'application'
|
||||
description = 'Delegates to :stirling-pdf:bootRun'
|
||||
dependsOn ':stirling-pdf:bootRun'
|
||||
|
||||
doFirst {
|
||||
println "Delegating to :stirling-pdf:bootRun"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('build') {
|
||||
group = 'build'
|
||||
description = 'Delegates to :stirling-pdf:bootJar'
|
||||
dependsOn ':stirling-pdf:bootJar'
|
||||
|
||||
doFirst {
|
||||
println "Delegating to :stirling-pdf:bootJar"
|
||||
}
|
||||
}
|
||||
|
196
common/.gitignore
vendored
196
common/.gitignore
vendored
@ -1,196 +0,0 @@
|
||||
### 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
|
||||
/common/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/
|
@ -1,20 +0,0 @@
|
||||
// Configure bootRun to disable it or point to a main class
|
||||
bootRun {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
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 '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'
|
||||
api "org.apache.pdfbox:pdfbox:$pdfboxVersion"
|
||||
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"
|
||||
api 'jakarta.mail:jakarta.mail-api:2.1.3'
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package stirling.software.common.model.api.converters;
|
||||
|
||||
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 EmlToPdfRequest extends PDFFile {
|
||||
|
||||
// fileInput is inherited from PDFFile
|
||||
|
||||
@Schema(
|
||||
description = "Include email attachments in the PDF output",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
|
||||
example = "false")
|
||||
private boolean includeAttachments = false;
|
||||
|
||||
@Schema(
|
||||
description = "Maximum attachment size in MB to include (default 10MB, range: 1-100)",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
|
||||
example = "10",
|
||||
minimum = "1",
|
||||
maximum = "100")
|
||||
private int maxAttachmentSizeMB = 10;
|
||||
|
||||
@Schema(
|
||||
description = "Download HTML intermediate file instead of PDF",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
|
||||
example = "false")
|
||||
private boolean downloadHtml = false;
|
||||
|
||||
@Schema(
|
||||
description = "Include CC and BCC recipients in header (if available)",
|
||||
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
|
||||
example = "true")
|
||||
private boolean includeAllRecipients = true;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package stirling.software.common.model.exception;
|
||||
|
||||
public class UnsupportedClaimException extends RuntimeException {
|
||||
public UnsupportedClaimException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,14 +0,0 @@
|
||||
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,14 +0,0 @@
|
||||
package stirling.software.common.util;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class ValidationUtils {
|
||||
|
||||
public static boolean isStringEmpty(String input) {
|
||||
return input == null || input.isBlank();
|
||||
}
|
||||
|
||||
public static boolean isCollectionEmpty(Collection<String> input) {
|
||||
return input == null || input.isEmpty();
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ services:
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
- ../testing/allEndpointsRemovedSettings.yml:/configs/settings.yml:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "false"
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
PUID: 1002
|
||||
PGID: 1002
|
||||
|
@ -20,7 +20,7 @@ services:
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "false"
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
PUID: 1002
|
||||
PGID: 1002
|
||||
|
@ -18,7 +18,7 @@ services:
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "false"
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
PUID: 1002
|
||||
PGID: 1002
|
||||
|
@ -14,11 +14,11 @@ 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"
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
SECURITY_OAUTH2_ENABLED: "true"
|
||||
SECURITY_OAUTH2_AUTOCREATEUSER: "true" # This is set to true to allow auto-creation of non-existing users in Stirling-PDF
|
||||
|
@ -18,7 +18,7 @@ services:
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DISABLE_ADDITIONAL_FEATURES: "false"
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
PUID: 1002
|
||||
PGID: 1002
|
||||
|
@ -14,11 +14,11 @@ 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"
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
SYSTEM_DEFAULTLOCALE: en-US
|
||||
UI_APPNAME: Stirling-PDF-Lite
|
||||
|
@ -14,10 +14,10 @@ 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"
|
||||
DOCKER_ENABLE_SECURITY: "false"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
SYSTEM_DEFAULTLOCALE: en-US
|
||||
UI_APPNAME: Stirling-PDF-Ultra-lite
|
||||
|
@ -14,11 +14,11 @@ 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"
|
||||
DOCKER_ENABLE_SECURITY: "false"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
LANGS: "en_GB,en_US,ar_AR,de_DE,fr_FR,es_ES,zh_CN,zh_TW,ca_CA,it_IT,sv_SE,pl_PL,ro_RO,ko_KR,pt_BR,ru_RU,el_GR,hi_IN,hu_HU,tr_TR,id_ID"
|
||||
SYSTEM_DEFAULTLOCALE: en-US
|
||||
|
@ -14,11 +14,11 @@ 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"
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
PUID: 1002
|
||||
PGID: 1002
|
||||
|
196
proprietary/.gitignore
vendored
196
proprietary/.gitignore
vendored
@ -1,196 +0,0 @@
|
||||
### 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
|
||||
/proprietary/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/
|
@ -1,51 +0,0 @@
|
||||
Stirling PDF User License
|
||||
|
||||
Copyright (c) 2025 Stirling PDF Inc.
|
||||
|
||||
License Scope & Usage Rights
|
||||
|
||||
Production use of the Stirling PDF Software is only permitted with a valid Stirling PDF User License.
|
||||
|
||||
For purposes of this license, “the Software” refers to the Stirling PDF application and any associated documentation files
|
||||
provided by Stirling PDF Inc. You or your organization may not use the Software in production, at scale, or for business-critical
|
||||
processes unless you have agreed to, and remain in compliance with, the Stirling PDF Subscription Terms of Service
|
||||
(https://www.stirlingpdf.com/terms) or another valid agreement with Stirling PDF, and hold an active User License subscription
|
||||
covering the appropriate number of licensed users.
|
||||
|
||||
Trial and Minimal Use
|
||||
|
||||
You may use the Software without a paid subscription for the sole purposes of internal trial, evaluation, or minimal use, provided that:
|
||||
* Use is limited to the capabilities and restrictions defined by the Software itself;
|
||||
* You do not copy, distribute, sublicense, reverse-engineer, or use the Software in client-facing or commercial contexts.
|
||||
|
||||
Continued use beyond this scope requires a valid Stirling PDF User License.
|
||||
|
||||
Modifications and Derivative Works
|
||||
|
||||
You may modify the Software only for development or internal testing purposes. Any such modifications or derivative works:
|
||||
|
||||
* May not be deployed in production environments without a valid User License;
|
||||
* May not be distributed or sublicensed;
|
||||
* Remain the intellectual property of Stirling PDF and/or its licensors;
|
||||
* May only be used, copied, or exploited in accordance with the terms of a valid Stirling PDF User License subscription.
|
||||
|
||||
Prohibited Actions
|
||||
|
||||
Unless explicitly permitted by a paid license or separate agreement, you may not:
|
||||
|
||||
* Use the Software in production environments;
|
||||
* Copy, merge, distribute, sublicense, or sell the Software;
|
||||
* Remove or alter any licensing or copyright notices;
|
||||
* Circumvent access restrictions or licensing requirements.
|
||||
|
||||
Third-Party Components
|
||||
|
||||
The Stirling PDF Software may include components subject to separate open source licenses. Such components remain governed by
|
||||
their original license terms as provided by their respective owners.
|
||||
|
||||
Disclaimer
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,39 +0,0 @@
|
||||
repositories {
|
||||
maven { url = "https://build.shibboleth.net/maven/releases" }
|
||||
}
|
||||
bootRun {
|
||||
enabled = false
|
||||
}
|
||||
dependencies {
|
||||
implementation project(':common')
|
||||
|
||||
api 'org.springframework:spring-jdbc'
|
||||
api 'org.springframework:spring-webmvc'
|
||||
api 'org.springframework.session:spring-session-core'
|
||||
api "org.springframework.security:spring-security-core:$springSecuritySamlVersion"
|
||||
api "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion"
|
||||
api 'org.springframework.boot:spring-boot-starter-jetty'
|
||||
api 'org.springframework.boot:spring-boot-starter-security'
|
||||
api 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
api 'org.springframework.boot:spring-boot-starter-oauth2-client'
|
||||
api 'org.springframework.boot:spring-boot-starter-mail'
|
||||
api 'io.swagger.core.v3:swagger-core-jakarta:2.2.30'
|
||||
implementation 'com.bucket4j:bucket4j_jdk17-core:8.14.0'
|
||||
|
||||
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
|
||||
implementation 'org.bouncycastle:bcprov-jdk18on:1.80'
|
||||
|
||||
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'
|
||||
runtimeOnly 'com.h2database:h2:2.3.232' // Don't upgrade h2database
|
||||
runtimeOnly 'org.postgresql:postgresql:42.7.5'
|
||||
constraints {
|
||||
implementation "org.opensaml:opensaml-core:$openSamlVersion"
|
||||
implementation "org.opensaml:opensaml-saml-api:$openSamlVersion"
|
||||
implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion"
|
||||
}
|
||||
implementation 'com.coveo:saml-client:5.0.0'
|
||||
}
|
||||
|
||||
tasks.register('prepareKotlinBuildScriptModel') {}
|
@ -1,44 +0,0 @@
|
||||
package stirling.software.proprietary.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
|
||||
@Entity
|
||||
@Table(name = "teams")
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
@ToString(onlyExplicitlyIncluded = true)
|
||||
public class Team implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "team_id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "name", unique = true, nullable = false)
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<User> users = new HashSet<>();
|
||||
|
||||
public void addUser(User user) {
|
||||
users.add(user);
|
||||
user.setTeam(this);
|
||||
}
|
||||
|
||||
public void removeUser(User user) {
|
||||
users.remove(user);
|
||||
user.setTeam(null);
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package stirling.software.proprietary.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class TeamWithUserCountDTO {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Long userCount;
|
||||
|
||||
// Constructor for JPQL projection
|
||||
public TeamWithUserCountDTO(Long id, String name, Long userCount) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.userCount = userCount;
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package stirling.software.proprietary.security.config;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/** Annotation to mark endpoints that require a Pro or higher license. */
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PremiumEndpoint {}
|
@ -1,30 +0,0 @@
|
||||
package stirling.software.proprietary.security.config;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
public class PremiumEndpointAspect {
|
||||
|
||||
private final boolean runningProOrHigher;
|
||||
|
||||
public PremiumEndpointAspect(@Qualifier("runningProOrHigher") boolean runningProOrHigher) {
|
||||
this.runningProOrHigher = runningProOrHigher;
|
||||
}
|
||||
|
||||
@Around(
|
||||
"@annotation(stirling.software.proprietary.security.config.PremiumEndpoint) || @within(stirling.software.proprietary.security.config.PremiumEndpoint)")
|
||||
public Object checkPremiumAccess(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
if (!runningProOrHigher) {
|
||||
throw new ResponseStatusException(
|
||||
HttpStatus.FORBIDDEN, "This endpoint requires a Pro or higher license");
|
||||
}
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
package stirling.software.proprietary.security.controller.api;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.config.PremiumEndpoint;
|
||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
import stirling.software.proprietary.security.service.TeamService;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/api/v1/team")
|
||||
@Tag(name = "Team", description = "Team Management APIs")
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@PremiumEndpoint
|
||||
public class TeamController {
|
||||
|
||||
private final TeamRepository teamRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PostMapping("/create")
|
||||
public RedirectView createTeam(@RequestParam("name") String name) {
|
||||
if (teamRepository.existsByNameIgnoreCase(name)) {
|
||||
return new RedirectView("/adminSettings?messageType=teamExists");
|
||||
}
|
||||
Team team = new Team();
|
||||
team.setName(name);
|
||||
teamRepository.save(team);
|
||||
return new RedirectView("/adminSettings?messageType=teamCreated");
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PostMapping("/rename")
|
||||
public RedirectView renameTeam(
|
||||
@RequestParam("teamId") Long teamId, @RequestParam("newName") String newName) {
|
||||
Optional<Team> existing = teamRepository.findById(teamId);
|
||||
if (existing.isEmpty()) {
|
||||
return new RedirectView("/adminSettings?messageType=teamNotFound");
|
||||
}
|
||||
if (teamRepository.existsByNameIgnoreCase(newName)) {
|
||||
return new RedirectView("/adminSettings?messageType=teamNameExists");
|
||||
}
|
||||
Team team = existing.get();
|
||||
|
||||
// Prevent renaming the Internal team
|
||||
if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
||||
return new RedirectView("/adminSettings?messageType=internalTeamNotAccessible");
|
||||
}
|
||||
|
||||
team.setName(newName);
|
||||
teamRepository.save(team);
|
||||
return new RedirectView("/adminSettings?messageType=teamRenamed");
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PostMapping("/delete")
|
||||
@Transactional
|
||||
public RedirectView deleteTeam(@RequestParam("teamId") Long teamId) {
|
||||
Optional<Team> teamOpt = teamRepository.findById(teamId);
|
||||
if (teamOpt.isEmpty()) {
|
||||
return new RedirectView("/adminSettings?messageType=teamNotFound");
|
||||
}
|
||||
|
||||
Team team = teamOpt.get();
|
||||
|
||||
// Prevent deleting the Internal team
|
||||
if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
||||
return new RedirectView("/adminSettings?messageType=internalTeamNotAccessible");
|
||||
}
|
||||
|
||||
long memberCount = userRepository.countByTeam(team);
|
||||
if (memberCount > 0) {
|
||||
return new RedirectView("/adminSettings?messageType=teamHasUsers");
|
||||
}
|
||||
|
||||
teamRepository.delete(team);
|
||||
return new RedirectView("/adminSettings?messageType=teamDeleted");
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@PostMapping("/addUser")
|
||||
@Transactional
|
||||
public RedirectView addUserToTeam(
|
||||
@RequestParam("teamId") Long teamId,
|
||||
@RequestParam("userId") Long userId) {
|
||||
|
||||
// Find the team
|
||||
Team team = teamRepository.findById(teamId)
|
||||
.orElseThrow(() -> new RuntimeException("Team not found"));
|
||||
|
||||
// Prevent adding users to the Internal team
|
||||
if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
||||
return new RedirectView("/teams?error=internalTeamNotAccessible");
|
||||
}
|
||||
|
||||
// Find the user
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new RuntimeException("User not found"));
|
||||
|
||||
// Check if user is in the Internal team - prevent moving them
|
||||
if (user.getTeam() != null && user.getTeam().getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
||||
return new RedirectView("/teams/" + teamId + "?error=cannotMoveInternalUsers");
|
||||
}
|
||||
|
||||
// Assign user to team
|
||||
user.setTeam(team);
|
||||
userRepository.save(user);
|
||||
|
||||
// Redirect back to team details page
|
||||
return new RedirectView("/teams/" + teamId + "?messageType=userAdded");
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
package stirling.software.proprietary.security.controller.web;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.model.dto.TeamWithUserCountDTO;
|
||||
import stirling.software.proprietary.security.database.repository.SessionRepository;
|
||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
import stirling.software.proprietary.security.service.TeamService;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/teams")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class TeamWebController {
|
||||
|
||||
private final TeamRepository teamRepository;
|
||||
private final SessionRepository sessionRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
public String listTeams(Model model) {
|
||||
// Get teams with user counts using a DTO projection
|
||||
List<TeamWithUserCountDTO> allTeamsWithCounts = teamRepository.findAllTeamsWithUserCount();
|
||||
|
||||
// Filter out the Internal team
|
||||
List<TeamWithUserCountDTO> teamsWithCounts = allTeamsWithCounts.stream()
|
||||
.filter(team -> !team.getName().equals(TeamService.INTERNAL_TEAM_NAME))
|
||||
.toList();
|
||||
|
||||
// Get the latest activity for each team
|
||||
List<Object[]> teamActivities = sessionRepository.findLatestActivityByTeam();
|
||||
|
||||
// Convert the query results to a map for easy access in the view
|
||||
Map<Long, Date> teamLastRequest = new HashMap<>();
|
||||
for (Object[] result : teamActivities) {
|
||||
Long teamId = (Long) result[0]; // teamId alias
|
||||
Date lastActivity = (Date) result[1]; // lastActivity alias
|
||||
teamLastRequest.put(teamId, lastActivity);
|
||||
}
|
||||
|
||||
// Add data to the model
|
||||
model.addAttribute("teamsWithCounts", teamsWithCounts);
|
||||
model.addAttribute("teamLastRequest", teamLastRequest);
|
||||
|
||||
return "accounts/teams";
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
public String viewTeamDetails(@PathVariable("id") Long id, Model model) {
|
||||
// Get the team
|
||||
Team team = teamRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Team not found"));
|
||||
|
||||
// Prevent access to Internal team
|
||||
if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
||||
return "redirect:/teams?error=internalTeamNotAccessible";
|
||||
}
|
||||
|
||||
// Get users for this team directly using the direct query
|
||||
List<User> teamUsers = userRepository.findAllByTeamId(id);
|
||||
|
||||
// Get all users not in this team for the Add User to Team dropdown
|
||||
// Exclude users that are in the Internal team
|
||||
List<User> allUsers = userRepository.findAllWithTeam();
|
||||
List<User> availableUsers = allUsers.stream()
|
||||
.filter(user -> (user.getTeam() == null || !user.getTeam().getId().equals(id)) &&
|
||||
(user.getTeam() == null || !user.getTeam().getName().equals(TeamService.INTERNAL_TEAM_NAME)))
|
||||
.toList();
|
||||
|
||||
// Get the latest session for each user in the team
|
||||
List<Object[]> userSessions = sessionRepository.findLatestSessionByTeamId(id);
|
||||
|
||||
// Create a map of username to last request date
|
||||
Map<String, Date> userLastRequest = new HashMap<>();
|
||||
for (Object[] result : userSessions) {
|
||||
String username = (String) result[0]; // username alias
|
||||
Date lastRequest = (Date) result[1]; // lastRequest alias
|
||||
userLastRequest.put(username, lastRequest);
|
||||
}
|
||||
|
||||
model.addAttribute("team", team);
|
||||
model.addAttribute("teamUsers", teamUsers);
|
||||
model.addAttribute("availableUsers", availableUsers);
|
||||
model.addAttribute("userLastRequest", userLastRequest);
|
||||
return "accounts/team-details";
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package stirling.software.proprietary.security.model;
|
||||
|
||||
public enum AuthenticationType {
|
||||
WEB,
|
||||
SSO
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package stirling.software.proprietary.security.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.model.dto.TeamWithUserCountDTO;
|
||||
|
||||
@Repository
|
||||
public interface TeamRepository extends JpaRepository<Team, Long> {
|
||||
Optional<Team> findByName(String name);
|
||||
|
||||
@Query("SELECT new stirling.software.proprietary.model.dto.TeamWithUserCountDTO(t.id, t.name, COUNT(u)) " +
|
||||
"FROM Team t LEFT JOIN t.users u GROUP BY t.id, t.name")
|
||||
List<TeamWithUserCountDTO> findAllTeamsWithUserCount();
|
||||
|
||||
boolean existsByNameIgnoreCase(String name);
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package stirling.software.proprietary.security.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TeamService {
|
||||
|
||||
private final TeamRepository teamRepository;
|
||||
|
||||
public static final String DEFAULT_TEAM_NAME = "Default";
|
||||
public static final String INTERNAL_TEAM_NAME = "Internal";
|
||||
|
||||
public Team getOrCreateDefaultTeam() {
|
||||
return teamRepository
|
||||
.findByName(DEFAULT_TEAM_NAME)
|
||||
.orElseGet(
|
||||
() -> {
|
||||
Team defaultTeam = new Team();
|
||||
defaultTeam.setName(DEFAULT_TEAM_NAME);
|
||||
return teamRepository.save(defaultTeam);
|
||||
});
|
||||
}
|
||||
|
||||
public Team getOrCreateInternalTeam() {
|
||||
return teamRepository
|
||||
.findByName(INTERNAL_TEAM_NAME)
|
||||
.orElseGet(
|
||||
() -> {
|
||||
Team internalTeam = new Team();
|
||||
internalTeam.setName(INTERNAL_TEAM_NAME);
|
||||
return teamRepository.save(internalTeam);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,387 +0,0 @@
|
||||
/* modern-tables.css - Professional styling for data tables and related elements */
|
||||
|
||||
/* Main container - Reduced max-width from 1100px to 900px */
|
||||
.data-container {
|
||||
max-width: 900px;
|
||||
margin: 2rem auto;
|
||||
background-color: var(--md-sys-color-surface-container-lowest);
|
||||
border-radius: 1rem;
|
||||
padding: 0.5rem;
|
||||
box-shadow: 0 2px 12px rgba(var(--md-sys-color-shadow, 0, 0, 0), 0.05);
|
||||
}
|
||||
|
||||
/* Panel / Card */
|
||||
.data-panel {
|
||||
background-color: var(--md-sys-color-surface);
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 2px 8px rgba(var(--md-sys-color-shadow, 0, 0, 0), 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.data-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1.25rem 1.5rem;
|
||||
background-color: var(--md-sys-color-surface-variant);
|
||||
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
||||
}
|
||||
|
||||
.data-title {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.data-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background-color: var(--md-sys-color-primary);
|
||||
color: var(--md-sys-color-on-primary);
|
||||
border-radius: 0.5rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* Content area */
|
||||
.data-body {
|
||||
padding: 1.5rem;
|
||||
background-color: var(--md-sys-color-surface-container-low);
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
/* Action buttons container */
|
||||
.data-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 1rem 0 1.5rem;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
/* Can add these classes for different alignments */
|
||||
.data-actions-start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.data-actions-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* Button styling */
|
||||
.data-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Fixed button colors - normal state has more contrast now */
|
||||
.data-btn-primary {
|
||||
background-color: var(--md-sys-color-primary);
|
||||
color: var(--md-sys-color-on-primary);
|
||||
}
|
||||
|
||||
.data-btn-primary:hover {
|
||||
background-color: var(--md-sys-color-primary-container);
|
||||
color: var(--md-sys-color-primary);
|
||||
box-shadow: 0 2px 4px rgba(var(--md-sys-color-shadow, 0, 0, 0), 0.1);
|
||||
}
|
||||
|
||||
.data-btn-secondary {
|
||||
background-color: var(--md-sys-color-secondary);
|
||||
color: var(--md-sys-color-on-secondary);
|
||||
}
|
||||
|
||||
.data-btn-secondary:hover {
|
||||
background-color: var(--md-sys-color-secondary-container);
|
||||
color: var(--md-sys-color-secondary);
|
||||
box-shadow: 0 2px 4px rgba(var(--md-sys-color-shadow, 0, 0, 0), 0.1);
|
||||
}
|
||||
|
||||
.data-btn-danger {
|
||||
background-color: var(--md-sys-color-error);
|
||||
color: var(--md-sys-color-on-error);
|
||||
}
|
||||
|
||||
.data-btn-danger:hover {
|
||||
background-color: var(--md-sys-color-error-container);
|
||||
color: var(--md-sys-color-error);
|
||||
box-shadow: 0 2px 4px rgba(var(--md-sys-color-shadow, 0, 0, 0), 0.1);
|
||||
}
|
||||
|
||||
.data-btn-sm {
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Icon button */
|
||||
.data-icon-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
border-radius: 0.5rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* Fixed icon button colors */
|
||||
.data-icon-btn-primary {
|
||||
background-color: var(--md-sys-color-primary);
|
||||
color: var(--md-sys-color-on-primary);
|
||||
}
|
||||
|
||||
.data-icon-btn-primary:hover {
|
||||
background-color: var(--md-sys-color-primary-container);
|
||||
color: var(--md-sys-color-primary);
|
||||
box-shadow: 0 2px 4px rgba(var(--md-sys-color-shadow, 0, 0, 0), 0.1);
|
||||
}
|
||||
|
||||
.data-icon-btn-danger {
|
||||
background-color: var(--md-sys-color-error);
|
||||
color: var(--md-sys-color-on-error);
|
||||
}
|
||||
|
||||
.data-icon-btn-danger:hover {
|
||||
background-color: var(--md-sys-color-error-container);
|
||||
color: var(--md-sys-color-error);
|
||||
box-shadow: 0 2px 4px rgba(var(--md-sys-color-shadow, 0, 0, 0), 0.1);
|
||||
}
|
||||
|
||||
/* Table styling */
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.data-table th {
|
||||
text-align: left;
|
||||
padding: 1rem;
|
||||
background-color: var(--md-sys-color-surface-variant);
|
||||
color: var(--md-sys-color-on-surface-variant);
|
||||
font-weight: 600;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.data-table th:first-child {
|
||||
border-top-left-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.data-table th:last-child {
|
||||
border-top-right-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.data-table td {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
||||
}
|
||||
|
||||
.data-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.data-table tr:hover {
|
||||
background-color: rgba(var(--md-sys-color-surface-variant-rgb), 0.5);
|
||||
}
|
||||
|
||||
/* Table action cells */
|
||||
.data-action-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.data-action-cell-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.data-action-cell-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* Status indicators */
|
||||
.data-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
border-radius: 1rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.data-status-success {
|
||||
background-color: var(--md-sys-color-tertiary-container);
|
||||
color: var(--md-sys-color-tertiary);
|
||||
}
|
||||
|
||||
.data-status-danger {
|
||||
background-color: var(--md-sys-color-error-container);
|
||||
color: var(--md-sys-color-error);
|
||||
}
|
||||
|
||||
.data-status-warning {
|
||||
background-color: var(--md-sys-color-secondary-container);
|
||||
color: var(--md-sys-color-secondary);
|
||||
}
|
||||
|
||||
.data-status-info {
|
||||
background-color: var(--md-sys-color-primary-container);
|
||||
color: var(--md-sys-color-primary);
|
||||
}
|
||||
|
||||
/* Stats/Info container */
|
||||
.data-stats {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.data-stat-card {
|
||||
background-color: var(--md-sys-color-surface-variant);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.25rem;
|
||||
flex: 1;
|
||||
min-width: 180px;
|
||||
box-shadow: 0 2px 8px rgba(var(--md-sys-color-shadow, 0, 0, 0), 0.05);
|
||||
}
|
||||
|
||||
.data-stat-label {
|
||||
font-size: 0.875rem;
|
||||
color: var(--md-sys-color-on-surface-variant);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.data-stat-value {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--md-sys-color-on-surface);
|
||||
}
|
||||
|
||||
/* Section title */
|
||||
.data-section-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin: 1.5rem 0 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
||||
}
|
||||
|
||||
/* Empty state styling */
|
||||
.data-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 3rem;
|
||||
color: var(--md-sys-color-on-surface-variant);
|
||||
}
|
||||
|
||||
.data-empty-icon {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.data-empty-text {
|
||||
font-size: 1.125rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* Modal styling */
|
||||
.data-modal {
|
||||
border-radius: 0.75rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.data-modal-header {
|
||||
background-color: var(--md-sys-color-surface-variant);
|
||||
padding: 1.25rem;
|
||||
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.data-modal-title {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
/* Modal close button styling */
|
||||
.data-btn-close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 50%;
|
||||
background-color: var(--md-sys-color-surface-variant);
|
||||
color: var(--md-sys-color-on-surface-variant);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.data-btn-close:hover {
|
||||
background-color: var(--md-sys-color-surface-container-high);
|
||||
color: var(--md-sys-color-on-surface);
|
||||
}
|
||||
|
||||
.data-btn-close .material-symbols-rounded {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.data-modal-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.data-modal-footer {
|
||||
padding: 1rem 1.5rem;
|
||||
border-top: 1px solid var(--md-sys-color-outline-variant);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
/* Form elements */
|
||||
.data-form-group {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.data-form-label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.data-form-control {
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid
|
||||
}
|
@ -1,196 +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=#{team.details.title}, header=#{team.details.header})}"></th:block>
|
||||
<link rel="stylesheet" th:href="@{/css/modern-tables.css}">
|
||||
</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>
|
||||
|
||||
<div class="data-container">
|
||||
<div class="data-panel">
|
||||
<div class="data-header">
|
||||
<h1 class="data-title">
|
||||
<span class="data-icon">
|
||||
<span class="material-symbols-rounded">group</span>
|
||||
</span>
|
||||
<span th:text="'Team: ' + ${team.name}">Team Name</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="data-body">
|
||||
<div class="data-stats">
|
||||
<div class="data-stat-card">
|
||||
<div class="data-stat-label">Total Members:</div>
|
||||
<div class="data-stat-value" th:text="${teamUsers.size()}">1</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="data-actions data-actions-start">
|
||||
<a th:href="@{'/teams'}" class="data-btn data-btn-secondary">
|
||||
<span class="material-symbols-rounded">arrow_back</span>
|
||||
<span th:text="#{team.back}">Back to Teams</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="data-section-title">Members</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Username</th>
|
||||
<th>Role</th>
|
||||
<th scope="col" th:title="${@runningProOrHigher} ? #{adminUserSettings.lastRequest} : 'Pro feature'" class="text-overflow" th:text="#{adminUserSettings.lastRequest}">Last Request</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="user : ${teamUsers}">
|
||||
<td th:text="${user.id}">1</td>
|
||||
<td th:text="${user.username}">username</td>
|
||||
<td th:text="#{${user.roleName}}">Role</td>
|
||||
<td th:text="${@runningProOrHigher} ? (${userLastRequest[user.username] != null ? #dates.format(userLastRequest[user.username], 'yyyy-MM-dd HH:mm:ss') : 'N/A'}) : 'hidden'">2023-01-01 12:00:00</td>
|
||||
<td>
|
||||
<span th:if="${user.enabled}" class="data-status data-status-success">
|
||||
<span class="material-symbols-rounded">person</span>
|
||||
Enabled
|
||||
</span>
|
||||
<span th:unless="${user.enabled}" class="data-status data-status-danger">
|
||||
<span class="material-symbols-rounded">person_off</span>
|
||||
Disabled
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Empty state for when there are no team members -->
|
||||
<div th:if="${teamUsers.empty}" class="data-empty">
|
||||
<span class="material-symbols-rounded data-empty-icon">person_off</span>
|
||||
<p class="data-empty-text">This team has no members yet.</p>
|
||||
<button data-bs-toggle="modal" data-bs-target="#addUserToTeamModal" class="data-btn data-btn-primary">
|
||||
<span class="material-symbols-rounded">person_add</span>
|
||||
<span th:text="#{team.addUser}">Add User to Team</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Add button for non-empty teams too -->
|
||||
<div th:if="${!teamUsers.empty}" class="data-actions data-mt-3">
|
||||
<button data-bs-toggle="modal" data-bs-target="#addUserToTeamModal" class="data-btn data-btn-primary">
|
||||
<span class="material-symbols-rounded">person_add</span>
|
||||
<span th:text="#{team.addUser}">Add User to Team</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JavaScript for team warning -->
|
||||
<script th:inline="javascript">
|
||||
function checkUserTeam(userId) {
|
||||
// Clear any existing warning
|
||||
const warningDiv = document.getElementById('teamChangeWarning');
|
||||
const warningMessage = document.getElementById('warningMessage');
|
||||
const submitButton = document.getElementById('addUserSubmitBtn');
|
||||
|
||||
// Reset
|
||||
warningDiv.style.display = 'none';
|
||||
submitButton.onclick = null;
|
||||
|
||||
// Get the selected option
|
||||
const selectedOption = document.querySelector('#userId option[value="' + userId + '"]');
|
||||
if (!selectedOption) return;
|
||||
|
||||
// Get team data
|
||||
const currentTeam = selectedOption.getAttribute('data-team');
|
||||
const currentTeamId = selectedOption.getAttribute('data-team-id');
|
||||
const newTeamName = /*[[${team.name}]]*/ 'Current Team';
|
||||
|
||||
// If user is already in a team, show warning
|
||||
if (currentTeam && currentTeam.length > 0) {
|
||||
// Use internationalized message
|
||||
const warningTemplate = /*[[#{team.warning.moveUser}]]*/ 'Warning: This will move the user from "{0}" team to "{1}" team. Are you sure?';
|
||||
const formattedWarning = warningTemplate.replace('{0}', currentTeam).replace('{1}', newTeamName);
|
||||
warningMessage.textContent = formattedWarning;
|
||||
warningDiv.style.display = 'block';
|
||||
|
||||
// Add confirmation to submit button
|
||||
submitButton.onclick = function(e) {
|
||||
// Use internationalized message
|
||||
const confirmTemplate = /*[[#{team.confirm.moveUser}]]*/ 'Are you sure you want to move this user from "{0}" team to "{1}" team?';
|
||||
const formattedConfirm = confirmTemplate.replace('{0}', currentTeam).replace('{1}', newTeamName);
|
||||
if (!confirm(formattedConfirm)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add User to Team Modal -->
|
||||
<div class="modal fade" id="addUserToTeamModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<form th:action="@{'/api/v1/team/addUser'}" method="post" class="modal-content data-modal">
|
||||
<div class="data-modal-header">
|
||||
<h5 class="data-modal-title">
|
||||
<span class="data-icon">
|
||||
<span class="material-symbols-rounded">person_add</span>
|
||||
</span>
|
||||
<span th:text="#{team.addUser}">Add User to Team</span>
|
||||
</h5>
|
||||
<button type="button" class="data-btn-close" data-bs-dismiss="modal" aria-label="Close">
|
||||
<span class="material-symbols-rounded">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="data-modal-body">
|
||||
<input type="hidden" name="teamId" th:value="${team.id}" />
|
||||
|
||||
<div class="data-form-group">
|
||||
<label for="userId" class="data-form-label" th:text="#{team.selectUser}">Select User</label>
|
||||
<select name="userId" id="userId" class="data-form-control" required onchange="checkUserTeam(this.value)">
|
||||
<option value="" disabled selected th:text="#{selectFillter}">-- Select User --</option>
|
||||
<option th:each="user : ${availableUsers}"
|
||||
th:value="${user.id}"
|
||||
th:text="${user.username}"
|
||||
th:data-team="${user.team != null ? user.team.name : ''}"
|
||||
th:data-team-id="${user.team != null ? user.team.id : ''}">
|
||||
Username
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Warning message for users being moved between teams -->
|
||||
<div id="teamChangeWarning" class="alert alert-warning mt-3" style="display: none;">
|
||||
<span class="material-symbols-rounded">warning</span>
|
||||
<span id="warningMessage">Warning: This will move the user from their current team to this team.</span>
|
||||
</div>
|
||||
|
||||
<div class="data-form-actions">
|
||||
<button type="button" class="data-btn data-btn-secondary" data-bs-dismiss="modal">
|
||||
<span class="material-symbols-rounded">close</span>
|
||||
<span th:text="#{cancel}">Cancel</span>
|
||||
</button>
|
||||
<button type="submit" id="addUserSubmitBtn" class="data-btn data-btn-primary">
|
||||
<span class="material-symbols-rounded">check</span>
|
||||
<span th:text="#{team.addUser}">Add User</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,133 +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=#{adminUserSettings.manageTeams}, header=#{adminUserSettings.manageTeams})}"></th:block>
|
||||
<link rel="stylesheet" th:href="@{/css/modern-tables.css}">
|
||||
</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>
|
||||
|
||||
<div class="data-container">
|
||||
<div class="data-panel">
|
||||
<div class="data-header">
|
||||
<h1 class="data-title">
|
||||
<span class="data-icon">
|
||||
<span class="material-symbols-rounded">groups</span>
|
||||
</span>
|
||||
<span th:text="#{adminUserSettings.manageTeams}">Team Management</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="data-body">
|
||||
<!-- Back Button -->
|
||||
<div class="data-actions data-actions-start">
|
||||
<a href="/adminSettings" class="data-btn data-btn-secondary">
|
||||
<span class="material-symbols-rounded">arrow_back</span>
|
||||
<span th:text="#{back.toSettings}">Back to Settings</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Create New Team Button -->
|
||||
<div class="data-actions">
|
||||
<a href="#"
|
||||
th:data-bs-toggle="${@runningProOrHigher} ? 'modal' : null"
|
||||
th:data-bs-target="${@runningProOrHigher} ? '#addTeamModal' : null"
|
||||
th:class="${@runningProOrHigher} ? 'data-btn data-btn-primary' : 'data-btn data-btn-danger'"
|
||||
th:title="${@runningProOrHigher} ? #{adminUserSettings.createTeam} : #{enterpriseEdition.proTeamFeatureDisabled}">
|
||||
<span class="material-symbols-rounded">group_add</span>
|
||||
<span th:text="#{adminUserSettings.createTeam}">Create New Team</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Team Table -->
|
||||
<div class="table-responsive">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" th:text="#{adminUserSettings.teamName}">Team Name</th>
|
||||
<th scope="col" th:text="#{adminUserSettings.totalMembers}">Total Members</th>
|
||||
<th scope="col" th:title="${@runningProOrHigher} ? #{adminUserSettings.lastRequest} : 'Pro feature'" class="text-overflow" th:text="#{adminUserSettings.lastRequest}">Last Request</th>
|
||||
<th scope="col" th:text="#{adminUserSettings.actions}">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Try approach 1 - DTO projection -->
|
||||
<tr th:each="teamDto : ${teamsWithCounts}">
|
||||
<td th:text="${teamDto.name}"></td>
|
||||
<td th:text="${teamDto.userCount}"></td>
|
||||
<td th:text="${@runningProOrHigher} ? (${teamLastRequest[teamDto.id] != null ? #dates.format(teamLastRequest[teamDto.id], 'yyyy-MM-dd HH:mm:ss') : 'N/A'}) : 'hidden'"></td>
|
||||
<td>
|
||||
<div class="data-action-cell">
|
||||
<a th:href="@{'/teams/' + ${teamDto.id}}" class="data-btn data-btn-secondary data-btn-sm" th:title="#{adminUserSettings.viewTeam}">
|
||||
<span class="material-symbols-rounded">search</span> <span th:text="#{view}">View</span>
|
||||
</a>
|
||||
<form th:action="@{'/api/v1/team/delete'}" method="post" style="display:inline-block"
|
||||
onsubmit="return confirmDeleteTeam()">
|
||||
<input type="hidden" name="teamId" th:value="${teamDto.id}" />
|
||||
<button type="submit" class="data-btn data-btn-danger data-btn-sm"
|
||||
th:disabled="${!@runningProOrHigher}"
|
||||
th:classappend="${!@runningProOrHigher} ? 'disabled' : ''"
|
||||
th:title="${@runningProOrHigher} ? #{adminUserSettings.deleteTeam} : #{enterpriseEdition.proTeamFeatureDisabled}">
|
||||
<span class="material-symbols-rounded">delete</span> <span th:text="#{delete}">Delete</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Delete Confirmation Script -->
|
||||
<script th:inline="javascript">
|
||||
const confirmDeleteText = /*[[#{adminUserSettings.confirmDeleteTeam}]]*/ 'Are you sure you want to delete this team?';
|
||||
function confirmDeleteTeam() {
|
||||
return confirm(confirmDeleteText);
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Team Modal -->
|
||||
<div class="modal fade" id="addTeamModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<form th:action="@{'/api/v1/team/create'}" method="post" class="modal-content data-modal">
|
||||
<div class="data-modal-header">
|
||||
<h5 class="data-modal-title">
|
||||
<span class="data-icon">
|
||||
<span class="material-symbols-rounded">group_add</span>
|
||||
</span>
|
||||
<span th:text="#{adminUserSettings.createTeam}">Create Team</span>
|
||||
</h5>
|
||||
<button type="button" class="data-btn-close" data-bs-dismiss="modal" aria-label="Close">
|
||||
<span class="material-symbols-rounded">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="data-modal-body">
|
||||
<div class="data-form-group">
|
||||
<label for="teamName" class="data-form-label" th:text="#{adminUserSettings.teamName}">Team Name</label>
|
||||
<input type="text" name="name" id="teamName" class="data-form-control" required />
|
||||
</div>
|
||||
<div class="data-form-actions">
|
||||
<button type="button" class="data-btn data-btn-secondary" data-bs-dismiss="modal">
|
||||
<span class="material-symbols-rounded">close</span>
|
||||
<span th:text="#{cancel}">Cancel</span>
|
||||
</button>
|
||||
<button type="submit" class="data-btn data-btn-primary">
|
||||
<span class="material-symbols-rounded">check</span>
|
||||
<span th:text="#{adminUserSettings.createTeam}">Create</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,83 +0,0 @@
|
||||
package stirling.software.proprietary.security.service;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TeamServiceTest {
|
||||
|
||||
@Mock
|
||||
private TeamRepository teamRepository;
|
||||
|
||||
@InjectMocks
|
||||
private TeamService teamService;
|
||||
|
||||
@Test
|
||||
void getDefaultTeam() {
|
||||
var team = new Team();
|
||||
team.setName("Marleyans");
|
||||
|
||||
when(teamRepository.findByName(TeamService.DEFAULT_TEAM_NAME))
|
||||
.thenReturn(Optional.of(team));
|
||||
|
||||
Team result = teamService.getOrCreateDefaultTeam();
|
||||
|
||||
assertEquals(team, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDefaultTeam_whenRepositoryIsEmpty() {
|
||||
String teamName = "Default";
|
||||
var defaultTeam = new Team();
|
||||
defaultTeam.setId(1L);
|
||||
defaultTeam.setName(teamName);
|
||||
|
||||
when(teamRepository.findByName(teamName))
|
||||
.thenReturn(Optional.empty());
|
||||
when(teamRepository.save(any(Team.class))).thenReturn(defaultTeam);
|
||||
|
||||
Team result = teamService.getOrCreateDefaultTeam();
|
||||
|
||||
assertEquals(TeamService.DEFAULT_TEAM_NAME, result.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInternalTeam() {
|
||||
var team = new Team();
|
||||
team.setName("Eldians");
|
||||
|
||||
when(teamRepository.findByName(TeamService.INTERNAL_TEAM_NAME))
|
||||
.thenReturn(Optional.of(team));
|
||||
|
||||
Team result = teamService.getOrCreateInternalTeam();
|
||||
|
||||
assertEquals(team, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createInternalTeam_whenRepositoryIsEmpty() {
|
||||
String teamName = "Internal";
|
||||
Team internalTeam = new Team();
|
||||
internalTeam.setId(2L);
|
||||
internalTeam.setName(teamName);
|
||||
|
||||
when(teamRepository.findByName(teamName))
|
||||
.thenReturn(Optional.empty());
|
||||
when(teamRepository.save(any(Team.class))).thenReturn(internalTeam);
|
||||
when(teamRepository.findByName(TeamService.INTERNAL_TEAM_NAME))
|
||||
.thenReturn(Optional.empty());
|
||||
|
||||
Team result = teamService.getOrCreateInternalTeam();
|
||||
|
||||
assertEquals(internalTeam, result);
|
||||
}
|
||||
}
|
@ -1,317 +0,0 @@
|
||||
package stirling.software.proprietary.security.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.model.enumeration.Role;
|
||||
import stirling.software.common.model.exception.UnsupportedProviderException;
|
||||
import stirling.software.proprietary.model.Team;
|
||||
import stirling.software.proprietary.security.database.repository.AuthorityRepository;
|
||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
||||
import stirling.software.proprietary.security.model.AuthenticationType;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
||||
import stirling.software.proprietary.security.session.SessionPersistentRegistry;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class UserServiceTest {
|
||||
|
||||
@Mock
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Mock
|
||||
private TeamRepository teamRepository;
|
||||
|
||||
@Mock
|
||||
private AuthorityRepository authorityRepository;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Mock
|
||||
private MessageSource messageSource;
|
||||
|
||||
@Mock
|
||||
private SessionPersistentRegistry sessionPersistentRegistry;
|
||||
|
||||
@Mock
|
||||
private DatabaseServiceInterface databaseService;
|
||||
|
||||
@Mock
|
||||
private ApplicationProperties.Security.OAUTH2 oauth2Properties;
|
||||
|
||||
@InjectMocks
|
||||
private UserService userService;
|
||||
|
||||
private Team mockTeam;
|
||||
private User mockUser;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
mockTeam = new Team();
|
||||
mockTeam.setId(1L);
|
||||
mockTeam.setName("Test Team");
|
||||
|
||||
mockUser = new User();
|
||||
mockUser.setId(1L);
|
||||
mockUser.setUsername("testuser");
|
||||
mockUser.setEnabled(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_WithUsernameAndAuthenticationType_Success() throws Exception {
|
||||
// Given
|
||||
String username = "testuser";
|
||||
AuthenticationType authType = AuthenticationType.WEB;
|
||||
|
||||
when(teamRepository.findByName("Default")).thenReturn(Optional.of(mockTeam));
|
||||
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
||||
doNothing().when(databaseService).exportDatabase();
|
||||
|
||||
// When
|
||||
userService.saveUser(username, authType);
|
||||
|
||||
// Then
|
||||
verify(userRepository).save(any(User.class));
|
||||
verify(databaseService).exportDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_WithUsernamePasswordAndTeamId_Success() throws Exception {
|
||||
// Given
|
||||
String username = "testuser";
|
||||
String password = "password123";
|
||||
Long teamId = 1L;
|
||||
String encodedPassword = "encodedPassword123";
|
||||
|
||||
when(passwordEncoder.encode(password)).thenReturn(encodedPassword);
|
||||
when(teamRepository.findById(teamId)).thenReturn(Optional.of(mockTeam));
|
||||
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
||||
doNothing().when(databaseService).exportDatabase();
|
||||
|
||||
// When
|
||||
User result = userService.saveUser(username, password, teamId);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
verify(passwordEncoder).encode(password);
|
||||
verify(teamRepository).findById(teamId);
|
||||
verify(userRepository).save(any(User.class));
|
||||
verify(databaseService).exportDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_WithTeamAndRole_Success() throws Exception {
|
||||
// Given
|
||||
String username = "testuser";
|
||||
String password = "password123";
|
||||
String role = Role.ADMIN.getRoleId();
|
||||
boolean firstLogin = true;
|
||||
String encodedPassword = "encodedPassword123";
|
||||
|
||||
when(passwordEncoder.encode(password)).thenReturn(encodedPassword);
|
||||
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
||||
doNothing().when(databaseService).exportDatabase();
|
||||
|
||||
// When
|
||||
User result = userService.saveUser(username, password, mockTeam, role, firstLogin);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
verify(passwordEncoder).encode(password);
|
||||
verify(userRepository).save(any(User.class));
|
||||
verify(databaseService).exportDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_WithInvalidUsername_ThrowsException() throws Exception {
|
||||
// Given
|
||||
String invalidUsername = "ab"; // Too short (less than 3 characters)
|
||||
AuthenticationType authType = AuthenticationType.WEB;
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> userService.saveUser(invalidUsername, authType)
|
||||
);
|
||||
|
||||
verify(userRepository, never()).save(any(User.class));
|
||||
verify(databaseService, never()).exportDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_WithNullPassword_Success() throws Exception {
|
||||
// Given
|
||||
String username = "testuser";
|
||||
Long teamId = 1L;
|
||||
|
||||
when(teamRepository.findById(teamId)).thenReturn(Optional.of(mockTeam));
|
||||
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
||||
doNothing().when(databaseService).exportDatabase();
|
||||
|
||||
// When
|
||||
User result = userService.saveUser(username, null, teamId);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
verify(passwordEncoder, never()).encode(anyString());
|
||||
verify(userRepository).save(any(User.class));
|
||||
verify(databaseService).exportDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_WithEmptyPassword_Success() throws Exception {
|
||||
// Given
|
||||
String username = "testuser";
|
||||
String emptyPassword = "";
|
||||
Long teamId = 1L;
|
||||
|
||||
when(teamRepository.findById(teamId)).thenReturn(Optional.of(mockTeam));
|
||||
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
||||
doNothing().when(databaseService).exportDatabase();
|
||||
|
||||
// When
|
||||
User result = userService.saveUser(username, emptyPassword, teamId);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
verify(passwordEncoder, never()).encode(anyString());
|
||||
verify(userRepository).save(any(User.class));
|
||||
verify(databaseService).exportDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_WithValidEmail_Success() throws Exception {
|
||||
// Given
|
||||
String emailUsername = "test@example.com";
|
||||
AuthenticationType authType = AuthenticationType.SSO;
|
||||
|
||||
when(teamRepository.findByName("Default")).thenReturn(Optional.of(mockTeam));
|
||||
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
||||
doNothing().when(databaseService).exportDatabase();
|
||||
|
||||
// When
|
||||
userService.saveUser(emailUsername, authType);
|
||||
|
||||
// Then
|
||||
verify(userRepository).save(any(User.class));
|
||||
verify(databaseService).exportDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_WithReservedUsername_ThrowsException() throws Exception {
|
||||
// Given
|
||||
String reservedUsername = "all_users";
|
||||
AuthenticationType authType = AuthenticationType.WEB;
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> userService.saveUser(reservedUsername, authType)
|
||||
);
|
||||
|
||||
verify(userRepository, never()).save(any(User.class));
|
||||
verify(databaseService, never()).exportDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_WithAnonymousUser_ThrowsException() throws Exception {
|
||||
// Given
|
||||
String anonymousUsername = "anonymoususer";
|
||||
AuthenticationType authType = AuthenticationType.WEB;
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> userService.saveUser(anonymousUsername, authType)
|
||||
);
|
||||
|
||||
verify(userRepository, never()).save(any(User.class));
|
||||
verify(databaseService, never()).exportDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_DatabaseExportThrowsException_StillSavesUser() throws Exception {
|
||||
// Given
|
||||
String username = "testuser";
|
||||
String password = "password123";
|
||||
Long teamId = 1L;
|
||||
String encodedPassword = "encodedPassword123";
|
||||
|
||||
when(passwordEncoder.encode(password)).thenReturn(encodedPassword);
|
||||
when(teamRepository.findById(teamId)).thenReturn(Optional.of(mockTeam));
|
||||
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
||||
doThrow(new SQLException("Database export failed")).when(databaseService).exportDatabase();
|
||||
|
||||
// When & Then
|
||||
assertThrows(SQLException.class, () -> userService.saveUser(username, password, teamId));
|
||||
|
||||
// Verify user was still saved before the exception
|
||||
verify(userRepository).save(any(User.class));
|
||||
verify(databaseService).exportDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_WithFirstLoginFlag_Success() throws Exception {
|
||||
// Given
|
||||
String username = "testuser";
|
||||
String password = "password123";
|
||||
Long teamId = 1L;
|
||||
boolean firstLogin = true;
|
||||
boolean enabled = false;
|
||||
String encodedPassword = "encodedPassword123";
|
||||
|
||||
when(passwordEncoder.encode(password)).thenReturn(encodedPassword);
|
||||
when(teamRepository.findById(teamId)).thenReturn(Optional.of(mockTeam));
|
||||
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
||||
doNothing().when(databaseService).exportDatabase();
|
||||
|
||||
// When
|
||||
userService.saveUser(username, password, teamId, firstLogin, enabled);
|
||||
|
||||
// Then
|
||||
verify(passwordEncoder).encode(password);
|
||||
verify(userRepository).save(any(User.class));
|
||||
verify(databaseService).exportDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveUser_WithCustomRole_Success() throws Exception {
|
||||
// Given
|
||||
String username = "testuser";
|
||||
String password = "password123";
|
||||
Long teamId = 1L;
|
||||
String customRole = Role.LIMITED_API_USER.getRoleId();
|
||||
String encodedPassword = "encodedPassword123";
|
||||
|
||||
when(passwordEncoder.encode(password)).thenReturn(encodedPassword);
|
||||
when(teamRepository.findById(teamId)).thenReturn(Optional.of(mockTeam));
|
||||
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
||||
doNothing().when(databaseService).exportDatabase();
|
||||
|
||||
// When
|
||||
userService.saveUser(username, password, teamId, customRole);
|
||||
|
||||
// Then
|
||||
verify(passwordEncoder).encode(password);
|
||||
verify(userRepository).save(any(User.class));
|
||||
verify(databaseService).exportDatabase();
|
||||
}
|
||||
|
||||
}
|
@ -182,8 +182,7 @@ def compare_files(
|
||||
sort_ignore_translation[language]["ignore"].remove(
|
||||
default_key.strip()
|
||||
)
|
||||
except ValueError as e:
|
||||
print(f"Error processing line {line_num} in {file_path}: {e}")
|
||||
except ValueError:
|
||||
print(f"{line_default}|{line_file}")
|
||||
exit(1)
|
||||
except IndexError:
|
||||
@ -207,7 +206,7 @@ def compare_files(
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
directory = os.path.join(os.getcwd(), "stirling-pdf", "src", "main", "resources")
|
||||
directory = os.path.join(os.getcwd(), "src", "main", "resources")
|
||||
messages_file_paths = glob.glob(os.path.join(directory, "messages_*.properties"))
|
||||
reference_file = os.path.join(directory, "messages_en_GB.properties")
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
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
|
||||
if [ "$DISABLE_ADDITIONAL_FEATURES" = "false" ] && [ "$VERSION_TAG" != "alpha" ]; then
|
||||
echo "Running Stirling PDF with DOCKER_ENABLE_SECURITY=${DOCKER_ENABLE_SECURITY} and VERSION_TAG=${VERSION_TAG}"
|
||||
# Check for DOCKER_ENABLE_SECURITY and download the appropriate JAR if required
|
||||
if [ "$DOCKER_ENABLE_SECURITY" = "true" ] && [ "$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"
|
||||
curl -L -o app-security.jar https://files.stirlingpdf.com/v$VERSION_TAG/Stirling-PDF-with-login.jar
|
||||
|
@ -1,190 +1,34 @@
|
||||
[ar_AR]
|
||||
ignore = [
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.que',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[az_AZ]
|
||||
ignore = [
|
||||
'lang.afr',
|
||||
'lang.bre',
|
||||
'lang.div',
|
||||
'lang.epo',
|
||||
'lang.guj',
|
||||
'lang.hin',
|
||||
'lang.kan',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mlt',
|
||||
'lang.mri',
|
||||
'lang.msa',
|
||||
'lang.nep',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.san',
|
||||
'lang.sin',
|
||||
'lang.slk',
|
||||
'lang.snd',
|
||||
'lang.sun',
|
||||
'lang.tam',
|
||||
'lang.tat',
|
||||
'lang.urd',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[bg_BG]
|
||||
ignore = [
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.iku',
|
||||
'lang.que',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[bo_CN]
|
||||
ignore = [
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[ca_CA]
|
||||
ignore = [
|
||||
'PDFToText.tags',
|
||||
'adminUserSettings.admin',
|
||||
'lang.amh',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.fao',
|
||||
'lang.fry',
|
||||
'lang.guj',
|
||||
'lang.hin',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.kaz',
|
||||
'lang.lao',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.pus',
|
||||
'lang.que',
|
||||
'lang.snd',
|
||||
'lang.swa',
|
||||
'lang.tam',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgk',
|
||||
'lang.tgl',
|
||||
'lang.tir',
|
||||
'lang.uzb',
|
||||
'lang.uzb_cyrl',
|
||||
'language.direction',
|
||||
'watermark.type.1',
|
||||
]
|
||||
|
||||
[cs_CZ]
|
||||
ignore = [
|
||||
'lang.amh',
|
||||
'lang.asm',
|
||||
'lang.bod',
|
||||
'lang.bos',
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.frk',
|
||||
'lang.gla',
|
||||
'lang.guj',
|
||||
'lang.iku',
|
||||
'lang.jav',
|
||||
'lang.kan',
|
||||
'lang.kat',
|
||||
'lang.khm',
|
||||
'lang.kir',
|
||||
'lang.lao',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.msa',
|
||||
'lang.nor',
|
||||
'lang.oci',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.pus',
|
||||
'lang.que',
|
||||
'lang.san',
|
||||
'lang.sin',
|
||||
'lang.snd',
|
||||
'lang.sun',
|
||||
'lang.tam',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'lang.tha',
|
||||
'lang.tir',
|
||||
'lang.uig',
|
||||
'lang.urd',
|
||||
'lang.uzb',
|
||||
'lang.uzb_cyrl',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
'text',
|
||||
]
|
||||
|
||||
[da_DK]
|
||||
ignore = [
|
||||
'lang.afr',
|
||||
'lang.amh',
|
||||
'lang.ben',
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.frk',
|
||||
'lang.guj',
|
||||
'lang.hin',
|
||||
'lang.iku',
|
||||
'lang.jav',
|
||||
'lang.kan',
|
||||
'lang.khm',
|
||||
'lang.lao',
|
||||
'lang.lat',
|
||||
'lang.ltz',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.msa',
|
||||
'lang.nep',
|
||||
'lang.oci',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.pus',
|
||||
'lang.que',
|
||||
'lang.san',
|
||||
'lang.sin',
|
||||
'lang.slk_frak',
|
||||
'lang.snd',
|
||||
'lang.sun',
|
||||
'lang.swa',
|
||||
'lang.tam',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgk',
|
||||
'lang.tgl',
|
||||
'lang.tha',
|
||||
'lang.tir',
|
||||
'lang.ton',
|
||||
'lang.uig',
|
||||
'lang.urd',
|
||||
'lang.uzb',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
@ -197,36 +41,8 @@ ignore = [
|
||||
'addPageNumbers.selectText.3',
|
||||
'alphabet',
|
||||
'certSign.name',
|
||||
'cookieBanner.popUp.acceptAllBtn',
|
||||
'endpointStatistics.top10',
|
||||
'endpointStatistics.top20',
|
||||
'fileChooser.dragAndDrop',
|
||||
'home.pipeline.title',
|
||||
'lang.afr',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.guj',
|
||||
'lang.hin',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.nep',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.que',
|
||||
'lang.san',
|
||||
'lang.snd',
|
||||
'lang.tam',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'lang.tir',
|
||||
'lang.urd',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
'legal.impressum',
|
||||
'licenses.version',
|
||||
@ -240,19 +56,13 @@ ignore = [
|
||||
'validateSignature.cert.version',
|
||||
'validateSignature.status',
|
||||
'watermark.type.1',
|
||||
'endpointStatistics.top10',
|
||||
'endpointStatistics.top20',
|
||||
'cookieBanner.popUp.acceptAllBtn',
|
||||
]
|
||||
|
||||
[el_GR]
|
||||
ignore = [
|
||||
'lang.ceb',
|
||||
'lang.dzo',
|
||||
'lang.iku',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.que',
|
||||
'lang.sin',
|
||||
'lang.uig',
|
||||
'lang.uzb_cyrl',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
@ -260,31 +70,6 @@ ignore = [
|
||||
ignore = [
|
||||
'adminUserSettings.roles',
|
||||
'error',
|
||||
'lang.asm',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.fil',
|
||||
'lang.frm',
|
||||
'lang.guj',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.lao',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.oci',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.san',
|
||||
'lang.snd',
|
||||
'lang.sun',
|
||||
'lang.tam',
|
||||
'lang.tel',
|
||||
'lang.tir',
|
||||
'lang.urd',
|
||||
'lang.uzb',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
'no',
|
||||
'showJS.tags',
|
||||
@ -292,23 +77,6 @@ ignore = [
|
||||
|
||||
[eu_ES]
|
||||
ignore = [
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.guj',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.mal',
|
||||
'lang.pan',
|
||||
'lang.que',
|
||||
'lang.san',
|
||||
'lang.slv',
|
||||
'lang.snd',
|
||||
'lang.sqi',
|
||||
'lang.tat',
|
||||
'lang.tir',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
@ -328,31 +96,6 @@ ignore = [
|
||||
'alphabet',
|
||||
'compare.document.1',
|
||||
'compare.document.2',
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.eus',
|
||||
'lang.guj',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.kaz',
|
||||
'lang.khm',
|
||||
'lang.lao',
|
||||
'lang.ltz',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.oci',
|
||||
'lang.ori',
|
||||
'lang.que',
|
||||
'lang.san',
|
||||
'lang.snd',
|
||||
'lang.swa',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'lang.tir',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
'licenses.license',
|
||||
'licenses.module',
|
||||
@ -365,24 +108,6 @@ ignore = [
|
||||
|
||||
[ga_IE]
|
||||
ignore = [
|
||||
'lang.ceb',
|
||||
'lang.cos',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.guj',
|
||||
'lang.hat',
|
||||
'lang.iku',
|
||||
'lang.lao',
|
||||
'lang.oci',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.sin',
|
||||
'lang.snd',
|
||||
'lang.sun',
|
||||
'lang.tgk',
|
||||
'lang.tir',
|
||||
'lang.uig',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
@ -395,126 +120,22 @@ ignore = [
|
||||
ignore = [
|
||||
'PDFToBook.selectText.1',
|
||||
'home.pipeline.title',
|
||||
'lang.bod',
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.dzo',
|
||||
'lang.guj',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.lao',
|
||||
'lang.mri',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.pus',
|
||||
'lang.que',
|
||||
'lang.san',
|
||||
'lang.snd',
|
||||
'lang.tam',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'lang.tir',
|
||||
'language.direction',
|
||||
'showJS.tags',
|
||||
]
|
||||
|
||||
[hu_HU]
|
||||
ignore = [
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.fao',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.lao',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.ori',
|
||||
'lang.que',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[id_ID]
|
||||
ignore = [
|
||||
'lang.aze',
|
||||
'lang.aze_cyrl',
|
||||
'lang.bre',
|
||||
'lang.cat',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.cym',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.eus',
|
||||
'lang.fao',
|
||||
'lang.frk',
|
||||
'lang.guj',
|
||||
'lang.hin',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.kaz',
|
||||
'lang.kir',
|
||||
'lang.lao',
|
||||
'lang.lat',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.pus',
|
||||
'lang.que',
|
||||
'lang.slk_frak',
|
||||
'lang.snd',
|
||||
'lang.sun',
|
||||
'lang.swa',
|
||||
'lang.tam',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgk',
|
||||
'lang.tgl',
|
||||
'lang.tha',
|
||||
'lang.tir',
|
||||
'lang.uig',
|
||||
'lang.urd',
|
||||
'lang.uzb',
|
||||
'lang.uzb_cyrl',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[it_IT]
|
||||
ignore = [
|
||||
'lang.asm',
|
||||
'lang.aze',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.fao',
|
||||
'lang.guj',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.lao',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.pus',
|
||||
'lang.que',
|
||||
'lang.snd',
|
||||
'lang.swa',
|
||||
'lang.tam',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'lang.urd',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
'no',
|
||||
'password',
|
||||
@ -527,21 +148,11 @@ ignore = [
|
||||
|
||||
[ja_JP]
|
||||
ignore = [
|
||||
'lang.jav',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[ko_KR]
|
||||
ignore = [
|
||||
'lang.fao',
|
||||
'lang.pus',
|
||||
'lang.sun',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[ml_IN]
|
||||
ignore = [
|
||||
'lang.iku',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
@ -549,37 +160,6 @@ ignore = [
|
||||
ignore = [
|
||||
'compare.document.1',
|
||||
'compare.document.2',
|
||||
'lang.afr',
|
||||
'lang.asm',
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.fao',
|
||||
'lang.guj',
|
||||
'lang.hin',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.lao',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.oci',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.pus',
|
||||
'lang.que',
|
||||
'lang.sin',
|
||||
'lang.snd',
|
||||
'lang.sun',
|
||||
'lang.swa',
|
||||
'lang.tam',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'lang.ton',
|
||||
'lang.urd',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
'navbar.allTools',
|
||||
'sponsor',
|
||||
@ -590,49 +170,6 @@ ignore = [
|
||||
'PDFToBook.selectText.1',
|
||||
'adminUserSettings.admin',
|
||||
'info',
|
||||
'lang.afr',
|
||||
'lang.amh',
|
||||
'lang.ben',
|
||||
'lang.bos',
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.dan_frak',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.guj',
|
||||
'lang.hin',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.khm',
|
||||
'lang.lao',
|
||||
'lang.lat',
|
||||
'lang.ltz',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.msa',
|
||||
'lang.nep',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.pus',
|
||||
'lang.que',
|
||||
'lang.san',
|
||||
'lang.slk_frak',
|
||||
'lang.snd',
|
||||
'lang.swa',
|
||||
'lang.tam',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgk',
|
||||
'lang.tgl',
|
||||
'lang.tha',
|
||||
'lang.tir',
|
||||
'lang.ton',
|
||||
'lang.uig',
|
||||
'lang.urd',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
'oops',
|
||||
'sponsor',
|
||||
@ -641,148 +178,27 @@ ignore = [
|
||||
[pl_PL]
|
||||
ignore = [
|
||||
'PDFToBook.selectText.1',
|
||||
'lang.afr',
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.cos',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.fao',
|
||||
'lang.frk',
|
||||
'lang.guj',
|
||||
'lang.hat',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.khm',
|
||||
'lang.lao',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.nep',
|
||||
'lang.oci',
|
||||
'lang.ori',
|
||||
'lang.pus',
|
||||
'lang.que',
|
||||
'lang.snd',
|
||||
'lang.sun',
|
||||
'lang.swa',
|
||||
'lang.tam',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'lang.tir',
|
||||
'lang.uig',
|
||||
'lang.urd',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[pt_BR]
|
||||
ignore = [
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.fao',
|
||||
'lang.fil',
|
||||
'lang.frk',
|
||||
'lang.fry',
|
||||
'lang.guj',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.kir',
|
||||
'lang.mar',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.que',
|
||||
'lang.snd',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'lang.tir',
|
||||
'lang.uig',
|
||||
'lang.uzb',
|
||||
'lang.yid',
|
||||
'language.direction',
|
||||
'pipelineOptions.pipelineHeader',
|
||||
]
|
||||
|
||||
[pt_PT]
|
||||
ignore = [
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.fao',
|
||||
'lang.fil',
|
||||
'lang.frk',
|
||||
'lang.fry',
|
||||
'lang.guj',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.kir',
|
||||
'lang.mar',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.que',
|
||||
'lang.snd',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'lang.tir',
|
||||
'lang.uig',
|
||||
'lang.uzb',
|
||||
'lang.yid',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[ro_RO]
|
||||
ignore = [
|
||||
'lang.amh',
|
||||
'lang.asm',
|
||||
'lang.bod',
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.cos',
|
||||
'lang.deu_frak',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.est',
|
||||
'lang.fao',
|
||||
'lang.glg',
|
||||
'lang.guj',
|
||||
'lang.iku',
|
||||
'lang.jav',
|
||||
'lang.kan',
|
||||
'lang.lao',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.nep',
|
||||
'lang.oci',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.pus',
|
||||
'lang.slk_frak',
|
||||
'lang.snd',
|
||||
'lang.sun',
|
||||
'lang.swa',
|
||||
'lang.tam',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'lang.tir',
|
||||
'lang.urd',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[ru_RU]
|
||||
ignore = [
|
||||
'lang.iku',
|
||||
'lang.pus',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
@ -791,25 +207,6 @@ ignore = [
|
||||
'adminUserSettings.admin',
|
||||
'home.multiTool.title',
|
||||
'info',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.iku',
|
||||
'lang.kaz',
|
||||
'lang.mar',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.que',
|
||||
'lang.san',
|
||||
'lang.sin',
|
||||
'lang.snd',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgl',
|
||||
'lang.tir',
|
||||
'lang.urd',
|
||||
'lang.uzb',
|
||||
'language.direction',
|
||||
'navbar.sections.security',
|
||||
'text',
|
||||
@ -818,37 +215,6 @@ ignore = [
|
||||
|
||||
[sl_SI]
|
||||
ignore = [
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.eus',
|
||||
'lang.fao',
|
||||
'lang.frk',
|
||||
'lang.guj',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.lao',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.oci',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.que',
|
||||
'lang.slk',
|
||||
'lang.snd',
|
||||
'lang.sun',
|
||||
'lang.tam',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgk',
|
||||
'lang.tgl',
|
||||
'lang.tir',
|
||||
'lang.urd',
|
||||
'lang.uzb',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
@ -861,43 +227,11 @@ ignore = [
|
||||
|
||||
[sv_SE]
|
||||
ignore = [
|
||||
'lang.ben',
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.guj',
|
||||
'lang.hin',
|
||||
'lang.kan',
|
||||
'lang.lao',
|
||||
'lang.lat',
|
||||
'lang.mal',
|
||||
'lang.mri',
|
||||
'lang.ori',
|
||||
'lang.pan',
|
||||
'lang.que',
|
||||
'lang.san',
|
||||
'lang.slk_frak',
|
||||
'lang.snd',
|
||||
'lang.tam',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tir',
|
||||
'lang.urd',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[th_TH]
|
||||
ignore = [
|
||||
'lang.dzo',
|
||||
'lang.kir',
|
||||
'lang.pan',
|
||||
'lang.sin',
|
||||
'lang.slk_frak',
|
||||
'lang.tir',
|
||||
'lang.uzb_cyrl',
|
||||
'language.direction',
|
||||
'pipelineOptions.pipelineHeader',
|
||||
'showJS.tags',
|
||||
@ -905,111 +239,33 @@ ignore = [
|
||||
|
||||
[tr_TR]
|
||||
ignore = [
|
||||
'lang.afr',
|
||||
'lang.bre',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.fao',
|
||||
'lang.guj',
|
||||
'lang.kan',
|
||||
'lang.lao',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.msa',
|
||||
'lang.ori',
|
||||
'lang.pus',
|
||||
'lang.que',
|
||||
'lang.sin',
|
||||
'lang.slk',
|
||||
'lang.slk_frak',
|
||||
'lang.snd',
|
||||
'lang.tam',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgk',
|
||||
'lang.tgl',
|
||||
'lang.tir',
|
||||
'lang.urd',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[uk_UA]
|
||||
ignore = [
|
||||
'lang.iku',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[vi_VN]
|
||||
ignore = [
|
||||
'lang.amh',
|
||||
'lang.asm',
|
||||
'lang.aze',
|
||||
'lang.aze_cyrl',
|
||||
'lang.bos',
|
||||
'lang.bre',
|
||||
'lang.cat',
|
||||
'lang.ceb',
|
||||
'lang.chr',
|
||||
'lang.cos',
|
||||
'lang.div',
|
||||
'lang.dzo',
|
||||
'lang.epo',
|
||||
'lang.eus',
|
||||
'lang.fao',
|
||||
'lang.glg',
|
||||
'lang.guj',
|
||||
'lang.iku',
|
||||
'lang.kan',
|
||||
'lang.kaz',
|
||||
'lang.kir',
|
||||
'lang.lat',
|
||||
'lang.ltz',
|
||||
'lang.mal',
|
||||
'lang.mar',
|
||||
'lang.mri',
|
||||
'lang.msa',
|
||||
'lang.ori',
|
||||
'lang.pus',
|
||||
'lang.que',
|
||||
'lang.sin',
|
||||
'lang.slk',
|
||||
'lang.slk_frak',
|
||||
'lang.snd',
|
||||
'lang.swa',
|
||||
'lang.syr',
|
||||
'lang.tam',
|
||||
'lang.tat',
|
||||
'lang.tel',
|
||||
'lang.tgk',
|
||||
'lang.tir',
|
||||
'lang.uig',
|
||||
'lang.uzb',
|
||||
'lang.uzb_cyrl',
|
||||
'lang.yid',
|
||||
'lang.yor',
|
||||
'language.direction',
|
||||
'pipeline.title',
|
||||
'pipelineOptions.pipelineHeader',
|
||||
'showJS.tags',
|
||||
]
|
||||
|
||||
[zh_BO]
|
||||
ignore = [
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[zh_CN]
|
||||
ignore = [
|
||||
'lang.dzo',
|
||||
'lang.iku',
|
||||
'lang.que',
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[zh_TW]
|
||||
ignore = [
|
||||
'lang.dzo',
|
||||
'lang.iku',
|
||||
'lang.que',
|
||||
'language.direction',
|
||||
]
|
||||
|
@ -2,6 +2,4 @@ plugins {
|
||||
// Apply the foojay-resolver plugin to allow automatic download of JDKs
|
||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
|
||||
}
|
||||
rootProject.name = 'Stirling PDF'
|
||||
|
||||
include 'stirling-pdf', 'common', 'proprietary'
|
||||
rootProject.name = 'Stirling-PDF'
|
||||
|
@ -1,21 +1,21 @@
|
||||
package stirling.software.proprietary.security.configuration.ee;
|
||||
|
||||
import static stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
|
||||
package stirling.software.SPDF.EE;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.model.ApplicationProperties.EnterpriseEdition;
|
||||
import stirling.software.common.model.ApplicationProperties.Premium;
|
||||
import stirling.software.common.model.ApplicationProperties.Premium.ProFeatures.GoogleDrive;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.EnterpriseEdition;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Premium;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Premium.ProFeatures.GoogleDrive;
|
||||
|
||||
@Configuration
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@Slf4j
|
||||
public class EEAppConfig {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
@ -29,23 +29,17 @@ public class EEAppConfig {
|
||||
migrateEnterpriseSettingsToPremium(this.applicationProperties);
|
||||
}
|
||||
|
||||
@Profile("security")
|
||||
@Bean(name = "runningProOrHigher")
|
||||
@Primary
|
||||
public boolean runningProOrHigher() {
|
||||
return licenseKeyChecker.getPremiumLicenseEnabledResult() != License.NORMAL;
|
||||
}
|
||||
|
||||
@Profile("security")
|
||||
@Bean(name = "license")
|
||||
@Primary
|
||||
public String licenseType() {
|
||||
return licenseKeyChecker.getPremiumLicenseEnabledResult().name();
|
||||
}
|
||||
|
||||
@Profile("security")
|
||||
@Bean(name = "runningEE")
|
||||
@Primary
|
||||
public boolean runningEnterprise() {
|
||||
return licenseKeyChecker.getPremiumLicenseEnabledResult() == License.ENTERPRISE;
|
||||
}
|
||||
@ -55,9 +49,7 @@ public class EEAppConfig {
|
||||
return applicationProperties.getPremium().getProFeatures().isSsoAutoLogin();
|
||||
}
|
||||
|
||||
@Profile("security")
|
||||
@Bean(name = "GoogleDriveEnabled")
|
||||
@Primary
|
||||
public boolean googleDriveEnabled() {
|
||||
return runningProOrHigher()
|
||||
&& applicationProperties.getPremium().getProFeatures().getGoogleDrive().isEnabled();
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.proprietary.security.configuration.ee;
|
||||
package stirling.software.SPDF.EE;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
@ -19,15 +19,15 @@ import com.posthog.java.shaded.org.json.JSONObject;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class KeygenLicenseVerifier {
|
||||
|
||||
public enum License {
|
||||
enum License {
|
||||
NORMAL,
|
||||
PRO,
|
||||
ENTERPRISE
|
||||
@ -520,7 +520,7 @@ public class KeygenLicenseVerifier {
|
||||
|
||||
HttpResponse<String> response =
|
||||
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
log.debug("ValidateLicenseResponse body: {}", response.body());
|
||||
log.info("ValidateLicenseResponse body: {}", response.body());
|
||||
JsonNode jsonResponse = objectMapper.readTree(response.body());
|
||||
if (response.statusCode() == 200) {
|
||||
JsonNode metaNode = jsonResponse.path("meta");
|
||||
@ -529,9 +529,9 @@ public class KeygenLicenseVerifier {
|
||||
String detail = metaNode.path("detail").asText();
|
||||
String code = metaNode.path("code").asText();
|
||||
|
||||
log.info("License validity: {}", isValid);
|
||||
log.info("Validation detail: {}", detail);
|
||||
log.info("Validation code: {}", code);
|
||||
log.info("License validity: " + isValid);
|
||||
log.info("Validation detail: " + detail);
|
||||
log.info("Validation code: " + code);
|
||||
|
||||
// Check if the license itself has floating attribute
|
||||
JsonNode licenseAttrs = jsonResponse.path("data").path("attributes");
|
||||
@ -595,7 +595,7 @@ public class KeygenLicenseVerifier {
|
||||
.path("isEnterprise")
|
||||
.asBoolean(false);
|
||||
|
||||
log.debug(applicationProperties.toString());
|
||||
log.info(applicationProperties.toString());
|
||||
|
||||
} else {
|
||||
log.error("Error validating license. Status code: {}", response.statusCode());
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.proprietary.security.configuration.ee;
|
||||
package stirling.software.SPDF.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.common.model.ApplicationProperties;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
|
||||
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@Slf4j
|
||||
public class LicenseKeyChecker {
|
||||
|
||||
private static final String FILE_PREFIX = "file:";
|
@ -3,11 +3,11 @@ package stirling.software.SPDF.Factories;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.common.model.api.misc.HighContrastColorCombination;
|
||||
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
||||
import stirling.software.common.util.misc.CustomColorReplaceStrategy;
|
||||
import stirling.software.common.util.misc.InvertFullColorStrategy;
|
||||
import stirling.software.common.util.misc.ReplaceAndInvertColorStrategy;
|
||||
import stirling.software.SPDF.model.api.misc.HighContrastColorCombination;
|
||||
import stirling.software.SPDF.model.api.misc.ReplaceAndInvert;
|
||||
import stirling.software.SPDF.utils.misc.CustomColorReplaceStrategy;
|
||||
import stirling.software.SPDF.utils.misc.InvertFullColorStrategy;
|
||||
import stirling.software.SPDF.utils.misc.ReplaceAndInvertColorStrategy;
|
||||
|
||||
@Component
|
||||
public class ReplaceAndInvertColorFactory {
|
@ -11,6 +11,7 @@ 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.core.env.Environment;
|
||||
@ -24,37 +25,34 @@ 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;
|
||||
import stirling.software.common.util.UrlUtils;
|
||||
import stirling.software.SPDF.config.ConfigInitializer;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.utils.UrlUtils;
|
||||
|
||||
@Slf4j
|
||||
@EnableScheduling
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = {
|
||||
"stirling.software.SPDF",
|
||||
"stirling.software.common",
|
||||
"stirling.software.proprietary"
|
||||
})
|
||||
@SpringBootApplication
|
||||
public class SPDFApplication {
|
||||
|
||||
private static String serverPortStatic;
|
||||
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;
|
||||
@ -143,14 +141,9 @@ public class SPDFApplication {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
String baseUrl = appConfig.getBaseUrl();
|
||||
String contextPath = appConfig.getContextPath();
|
||||
String serverPort = appConfig.getServerPort();
|
||||
baseUrlStatic = baseUrl;
|
||||
contextPathStatic = contextPath;
|
||||
serverPortStatic = serverPort;
|
||||
baseUrlStatic = this.baseUrl;
|
||||
contextPathStatic = this.contextPath;
|
||||
String url = baseUrl + ":" + getStaticPort() + contextPath;
|
||||
|
||||
if (webBrowser != null
|
||||
&& Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
|
||||
webBrowser.initWebUI(url);
|
||||
@ -161,7 +154,6 @@ 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);
|
||||
@ -178,6 +170,17 @@ 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)
|
||||
@ -202,37 +205,17 @@ public class SPDFApplication {
|
||||
}
|
||||
|
||||
private static String[] getActiveProfile(String[] args) {
|
||||
// 1. Check for explicitly passed profiles
|
||||
if (args != null) {
|
||||
for (String arg : args) {
|
||||
if (arg.startsWith("--spring.profiles.active=")) {
|
||||
String[] provided = arg.substring(arg.indexOf('=') + 1).split(",");
|
||||
if (provided.length > 0) {
|
||||
log.info("#######0000000000000###############################");
|
||||
return provided;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("######################################");
|
||||
// 2. Detect if SecurityConfiguration is present on classpath
|
||||
if (isClassPresent(
|
||||
"stirling.software.proprietary.security.configuration.SecurityConfiguration")) {
|
||||
log.info("security");
|
||||
return new String[] {"security"};
|
||||
} else {
|
||||
log.info("default");
|
||||
if (args == null) {
|
||||
return new String[] {"default"};
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isClassPresent(String className) {
|
||||
try {
|
||||
Class.forName(className, false, SPDFApplication.class.getClassLoader());
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
for (String arg : args) {
|
||||
if (arg.contains("spring.profiles.active")) {
|
||||
return arg.substring(args[0].indexOf('=') + 1).split(", ");
|
||||
}
|
||||
}
|
||||
|
||||
return new String[] {"default"};
|
||||
}
|
||||
|
||||
public static String getStaticBaseUrl() {
|
@ -43,8 +43,8 @@ import me.friwi.jcefmaven.MavenCefAppHandlerAdapter;
|
||||
import me.friwi.jcefmaven.impl.progress.ConsoleProgressHandler;
|
||||
|
||||
import stirling.software.SPDF.UI.WebBrowser;
|
||||
import stirling.software.common.configuration.InstallationPathConfig;
|
||||
import stirling.software.common.util.UIScaling;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.utils.UIScaling;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
@ -15,7 +15,7 @@ import io.github.pixee.security.BoundedLineReader;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.util.UIScaling;
|
||||
import stirling.software.SPDF.utils.UIScaling;
|
||||
|
||||
@Slf4j
|
||||
public class LoadingWindow extends JDialog {
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.common.configuration;
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@ -8,45 +8,34 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Predicate;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.thymeleaf.spring6.SpringTemplateEngine;
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Configuration
|
||||
@Lazy
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class AppConfig {
|
||||
|
||||
private final Environment env;
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
@Getter
|
||||
@Value("${baseUrl:http://localhost}")
|
||||
private String baseUrl;
|
||||
|
||||
@Getter
|
||||
@Value("${server.servlet.context-path:/}")
|
||||
private String contextPath;
|
||||
|
||||
@Getter
|
||||
@Value("${server.port:8080}")
|
||||
private String serverPort;
|
||||
private final Environment env;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "system.customHTMLFiles", havingValue = "true")
|
||||
@ -144,24 +133,10 @@ public class AppConfig {
|
||||
}
|
||||
}
|
||||
|
||||
@ConditionalOnMissingClass("stirling.software.SPDF.config.security.SecurityConfiguration")
|
||||
@Bean(name = "activeSecurity")
|
||||
public boolean activeSecurity() {
|
||||
String disableAdditionalFeatures = env.getProperty("DISABLE_ADDITIONAL_FEATURES");
|
||||
|
||||
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(disableAdditionalFeatures);
|
||||
}
|
||||
|
||||
return env.getProperty("DOCKER_ENABLE_SECURITY", Boolean.class, true);
|
||||
}
|
||||
|
||||
@Bean(name = "missingActiveSecurity")
|
||||
@ConditionalOnMissingClass(
|
||||
"stirling.software.proprietary.security.configuration.SecurityConfiguration")
|
||||
public boolean missingActiveSecurity() {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Bean(name = "directoryFilter")
|
||||
@ -223,60 +198,9 @@ public class AppConfig {
|
||||
return applicationProperties.getAutomaticallyGenerated().getUUID();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationProperties.Security security() {
|
||||
return applicationProperties.getSecurity();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationProperties.Security.OAUTH2 oAuth2() {
|
||||
return applicationProperties.getSecurity().getOauth2();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationProperties.Premium premium() {
|
||||
return applicationProperties.getPremium();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationProperties.System system() {
|
||||
return applicationProperties.getSystem();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationProperties.Datasource datasource() {
|
||||
return applicationProperties.getSystem().getDatasource();
|
||||
}
|
||||
|
||||
|
||||
@Bean(name = "runningProOrHigher")
|
||||
@Profile("default")
|
||||
public boolean runningProOrHigher() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Bean(name = "runningEE")
|
||||
@Profile("default")
|
||||
public boolean runningEnterprise() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Bean(name = "GoogleDriveEnabled")
|
||||
@Profile("default")
|
||||
public boolean googleDriveEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Bean(name = "license")
|
||||
@Profile("default")
|
||||
public String licenseType() {
|
||||
return "NORMAL";
|
||||
}
|
||||
|
||||
|
||||
@Bean(name = "disablePixel")
|
||||
public boolean disablePixel() {
|
||||
return Boolean.parseBoolean(env.getProperty("DISABLE_PIXEL", "false"));
|
||||
return Boolean.getBoolean(env.getProperty("DISABLE_PIXEL"));
|
||||
}
|
||||
|
||||
@Bean(name = "machineType")
|
@ -5,8 +5,8 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import stirling.software.common.configuration.interfaces.ShowAdminInterface;
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.config.interfaces.ShowAdminInterface;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Configuration
|
||||
class AppUpdateService {
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.common.configuration;
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@ -13,8 +13,6 @@ import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.util.YamlHelper;
|
||||
|
||||
/**
|
||||
* A naive, line-based approach to merging "settings.yml" with "settings.yml.template" while
|
||||
* preserving exact whitespace, blank lines, and inline comments -- but we only rewrite the file if
|
||||
@ -78,7 +76,7 @@ public class ConfigInitializer {
|
||||
Path customSettingsPath = Paths.get(InstallationPathConfig.getCustomSettingsPath());
|
||||
if (Files.notExists(customSettingsPath)) {
|
||||
Files.createFile(customSettingsPath);
|
||||
log.info("Created custom_settings file: {}", customSettingsPath);
|
||||
log.info("Created custom_settings file: {}", customSettingsPath.toString());
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.proprietary.security.filter;
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -12,8 +12,6 @@ import jakarta.annotation.PostConstruct;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.configuration.RuntimePathConfig;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class ExternalAppDepConfig {
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.common.configuration;
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -13,7 +13,7 @@ import org.thymeleaf.templateresource.ITemplateResource;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.model.InputStreamTemplateResource;
|
||||
import stirling.software.SPDF.model.InputStreamTemplateResource;
|
||||
|
||||
@Slf4j
|
||||
public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateResolver {
|
@ -17,8 +17,8 @@ import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@ -59,7 +59,7 @@ public class InitialSetup {
|
||||
|
||||
public void initEnableCSRFSecurity() throws IOException {
|
||||
if (GeneralUtils.isVersionHigher(
|
||||
"0.46.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) {
|
||||
"0.36.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) {
|
||||
Boolean csrf = applicationProperties.getSecurity().getCsrfDisabled();
|
||||
if (!csrf) {
|
||||
GeneralUtils.saveKeyToSettings("security.csrfDisabled", false);
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.common.configuration;
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
@ -48,21 +48,24 @@ public class InstallationPathConfig {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
if (os.contains("win")) {
|
||||
return Paths.get(
|
||||
System.getenv("APPDATA"), // parent path
|
||||
"Stirling-PDF")
|
||||
System.getenv("APPDATA"), // parent path
|
||||
"Stirling-PDF")
|
||||
.toString()
|
||||
+ File.separator;
|
||||
} else if (os.contains("mac")) {
|
||||
return Paths.get(
|
||||
System.getProperty("user.home"),
|
||||
"Library",
|
||||
"Application Support",
|
||||
"Stirling-PDF")
|
||||
System.getProperty("user.home"),
|
||||
"Library",
|
||||
"Application Support",
|
||||
"Stirling-PDF")
|
||||
.toString()
|
||||
+ File.separator;
|
||||
} else {
|
||||
return Paths.get(
|
||||
System.getProperty("user.home"), // parent path
|
||||
".config",
|
||||
"Stirling-PDF")
|
||||
System.getProperty("user.home"), // parent path
|
||||
".config",
|
||||
"Stirling-PDF")
|
||||
.toString()
|
||||
+ File.separator;
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
@ -1,7 +1,5 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import stirling.software.common.configuration.InstallationPathConfig;
|
||||
|
||||
import ch.qos.logback.core.PropertyDefinerBase;
|
||||
|
||||
public class LogbackPropertyLoader extends PropertyDefinerBase {
|
@ -16,7 +16,7 @@ import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.common.util.RequestUriUtils;
|
||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
@ -13,7 +13,7 @@ import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.common.configuration;
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.common.configuration;
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package stirling.software.common.configuration;
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -9,9 +9,9 @@ import org.springframework.context.annotation.Configuration;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.model.ApplicationProperties.CustomPaths.Operations;
|
||||
import stirling.software.common.model.ApplicationProperties.CustomPaths.Pipeline;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.CustomPaths.Operations;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.CustomPaths.Pipeline;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
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