Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74a1d83f8d | ||
|
|
7a431f509e | ||
|
|
e1a320ac37 | ||
|
|
f879f5d533 | ||
|
|
e49ca245e5 | ||
|
|
198fc1ced3 |
1
.github/scripts/requirements_sync_readme.in
vendored
1
.github/scripts/requirements_sync_readme.in
vendored
@@ -1 +0,0 @@
|
||||
tomlkit
|
||||
10
.github/scripts/requirements_sync_readme.txt
vendored
10
.github/scripts/requirements_sync_readme.txt
vendored
@@ -1,10 +0,0 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --generate-hashes --output-file='.github\scripts\requirements_sync_readme.txt' '.github\scripts\requirements_sync_readme.in'
|
||||
#
|
||||
tomlkit==0.13.2 \
|
||||
--hash=sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde \
|
||||
--hash=sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79
|
||||
# via -r .github\scripts\requirements_sync_readme.in
|
||||
256
.github/workflows/multiOSReleases.yml
vendored
256
.github/workflows/multiOSReleases.yml
vendored
@@ -9,140 +9,24 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
read_versions:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.versionNumber.outputs.versionNumber }}
|
||||
versionMac: ${{ steps.versionNumberMac.outputs.versionNumberMac }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
# Get version number
|
||||
- name: Get version number
|
||||
id: versionNumber
|
||||
run: |
|
||||
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get version number mac
|
||||
id: versionNumberMac
|
||||
run: |
|
||||
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||
CURRENT_YEAR=$(date +'%Y')
|
||||
IFS='.' read -r -a VERSION_PARTS <<< "$VERSION"
|
||||
MAC_VERSION="$CURRENT_YEAR.${VERSION_PARTS[1]:-0}.${VERSION_PARTS[2]:-0}"
|
||||
echo "versionNumberMac=$MAC_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
build-portable:
|
||||
needs: read_versions
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- enable_security: true
|
||||
file_suffix: "with-login-"
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
with:
|
||||
java-version: "21"
|
||||
distribution: "temurin"
|
||||
|
||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||
with:
|
||||
gradle-version: 8.12
|
||||
|
||||
- name: Generate jar (With Security=${{ matrix.enable_security }})
|
||||
run: ./gradlew clean createExe
|
||||
env:
|
||||
DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }}
|
||||
STIRLING_PDF_DESKTOP_UI: false
|
||||
|
||||
- name: Rename binaries
|
||||
run: |
|
||||
mv ./build/launch4j/Stirling-PDF.exe ./win-Stirling-PDF-portable-Server-${{ matrix.file_suffix }}${{ needs.read_versions.outputs.version }}.exe
|
||||
mv ./build/libs/Stirling-PDF-${{ needs.read_versions.outputs.version }}.jar ./Stirling-PDF-${{ matrix.file_suffix }}${{ needs.read_versions.outputs.version }}.jar
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
name: stirling-${{ matrix.file_suffix }}binaries
|
||||
path: |
|
||||
./win-Stirling-PDF-portable-Server-${{ matrix.file_suffix }}${{ needs.read_versions.outputs.version }}.exe
|
||||
./Stirling-PDF-${{ matrix.file_suffix }}${{ needs.read_versions.outputs.version }}.jar
|
||||
|
||||
sign_verify-portable:
|
||||
needs: [build-portable, read_versions]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- enable_security: true
|
||||
file_suffix: "with-login-"
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: stirling-${{ matrix.file_suffix }}binaries
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: Upload signed artifacts
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
name: stirling-${{ matrix.file_suffix }}signed
|
||||
path: |
|
||||
./*
|
||||
!cosign.*
|
||||
|
||||
build-installers:
|
||||
needs: read_versions
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
extra: "-installer"
|
||||
platform: win-
|
||||
platform: win
|
||||
ext: exe
|
||||
# - os: macos-latest
|
||||
# extra: ""
|
||||
# platform: mac-
|
||||
# ext: dmg
|
||||
# - os: ubuntu-latest
|
||||
# extra: ""
|
||||
# platform: linux-
|
||||
# ext: deb
|
||||
#- os: macos-latest
|
||||
# platform: mac
|
||||
# ext: dmg
|
||||
#- os: ubuntu-latest
|
||||
# platform: linux
|
||||
# ext: deb
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
@@ -168,6 +52,24 @@ jobs:
|
||||
curl -L -o wix.exe https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe
|
||||
.\wix.exe /install /quiet
|
||||
|
||||
# Install Linux dependencies
|
||||
- name: Install Linux Dependencies
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y fakeroot rpm
|
||||
|
||||
# Get version number
|
||||
- name: Get version number
|
||||
id: versionNumber
|
||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Get version number mac
|
||||
id: versionNumberMac
|
||||
run: echo "versionNumberMac=$(./gradlew printMacVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
# Build installer
|
||||
- name: Build Installer
|
||||
run: ./gradlew build jpackage -x test --info
|
||||
@@ -181,107 +83,23 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
mv "./build/jpackage/Stirling-PDF-${{ needs.read_versions.outputs.version }}.exe" "${{ matrix.platform }}Stirling-PDF${{ matrix.extra }}-${{ needs.read_versions.outputs.version }}.${{ matrix.ext }}"
|
||||
mv "build/jpackage/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.exe" "Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}"
|
||||
elif [ "${{ matrix.os }}" = "macos-latest" ]; then
|
||||
mv "./build/jpackage/Stirling-PDF-${{ needs.read_versions.outputs.versionMac }}.dmg" "${{ matrix.platform }}Stirling-PDF${{ matrix.extra }}-${{ needs.read_versions.outputs.version }}.${{ matrix.ext }}"
|
||||
mv "build/jpackage/Stirling-PDF-${{ steps.versionNumberMac.outputs.versionNumberMac }}.dmg" "Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}-${{ matrix.platform }}.${{ matrix.ext }}"
|
||||
else
|
||||
mv "./build/jpackage/stirling-pdf_${{ needs.read_versions.outputs.version }}-1_amd64.deb" "${{ matrix.platform }}Stirling-PDF${{ matrix.extra }}-${{ needs.read_versions.outputs.version }}.${{ matrix.ext }}"
|
||||
mv "build/jpackage/stirling-pdf_${{ steps.versionNumber.outputs.versionNumber }}-1_amd64.deb" "Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}-${{ matrix.platform }}.${{ matrix.ext }}"
|
||||
fi
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
# Upload installer as artifact for testing
|
||||
- name: Upload Installer Artifact
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
||||
path: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
name: ${{ matrix.platform }}binaries
|
||||
path: |
|
||||
./${{ matrix.platform }}Stirling-PDF${{ matrix.extra }}-${{ needs.read_versions.outputs.version }}.${{ matrix.ext }}
|
||||
|
||||
sign_verify:
|
||||
needs: [read_versions, build-installers]
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
extra: "-installer"
|
||||
platform: win-
|
||||
ext: exe
|
||||
# - os: macos-latest
|
||||
# extra: ""
|
||||
# platform: mac-
|
||||
# ext: dmg
|
||||
# - os: ubuntu-latest
|
||||
# extra: ""
|
||||
# platform: linux-
|
||||
# ext: deb
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: ${{ matrix.platform }}binaries
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: Install Cosign
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||
|
||||
- name: Generate key pair
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: cosign generate-key-pair
|
||||
|
||||
- name: Sign and generate attestations
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
cosign sign-blob \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-signature ./${{ matrix.platform }}Stirling-PDF${{ matrix.extra }}-${{ needs.read_versions.outputs.version }}.${{ matrix.ext }}.sig \
|
||||
./${{ matrix.platform }}Stirling-PDF${{ matrix.extra }}-${{ needs.read_versions.outputs.version }}.${{ matrix.ext }}
|
||||
|
||||
cosign attest-blob \
|
||||
--predicate - \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-attestation ./${{ matrix.platform }}Stirling-PDF${{ matrix.extra }}-${{ needs.read_versions.outputs.version }}.${{ matrix.ext }}.intoto.jsonl \
|
||||
./${{ matrix.platform }}Stirling-PDF${{ matrix.extra }}-${{ needs.read_versions.outputs.version }}.${{ matrix.ext }}
|
||||
|
||||
cosign verify-blob \
|
||||
--key ./cosign.pub \
|
||||
--signature ./${{ matrix.platform }}Stirling-PDF${{ matrix.extra }}-${{ needs.read_versions.outputs.version }}.${{ matrix.ext }}.sig \
|
||||
./${{ matrix.platform }}Stirling-PDF${{ matrix.extra }}-${{ needs.read_versions.outputs.version }}.${{ matrix.ext }}
|
||||
|
||||
- name: Upload signed artifacts
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
name: ${{ matrix.platform }}signed
|
||||
path: |
|
||||
./${{ matrix.platform }}Stirling-PDF${{ matrix.extra }}-${{ needs.read_versions.outputs.version }}.*
|
||||
!cosign.*
|
||||
|
||||
create-release:
|
||||
needs: [read_versions, sign_verify, sign_verify-portable]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Download signed artifacts
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
- name: Upload binaries, attestations and signatures to Release and create GitHub Release
|
||||
- name: Upload binaries to release
|
||||
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
||||
with:
|
||||
tag_name: v${{ needs.read_versions.outputs.version }}
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
./*signed/*
|
||||
files: ./Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
||||
|
||||
159
.github/workflows/releaseArtifacts.yml
vendored
159
.github/workflows/releaseArtifacts.yml
vendored
@@ -9,8 +9,11 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
strategy:
|
||||
matrix:
|
||||
enable_security: [true, false]
|
||||
@@ -19,8 +22,6 @@ jobs:
|
||||
file_suffix: "-with-login"
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
outputs:
|
||||
version: ${{ steps.versionNumber.outputs.versionNumber }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
@@ -47,134 +48,38 @@ jobs:
|
||||
|
||||
- name: Get version number
|
||||
id: versionNumber
|
||||
run: |
|
||||
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
|
||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Rename binaries
|
||||
run: |
|
||||
mv ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
mv ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
- name: Rename binarie
|
||||
run: cp ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
|
||||
- name: Debug build artifacts
|
||||
run: |
|
||||
echo "Current Directory: $(pwd)"
|
||||
ls -R ./build/libs
|
||||
ls -R ./build/launch4j
|
||||
|
||||
- name: Upload build artifacts
|
||||
- name: Upload Assets binarie
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: binaries${{ matrix.file_suffix }}
|
||||
path: |
|
||||
./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.*
|
||||
./build/libs/Stirling-PDF${{ matrix.file_suffix }}.*
|
||||
path: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
name: Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
overwrite: true
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
|
||||
sign_verify:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- enable_security: true
|
||||
file_suffix: "-with-login"
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: binaries${{ matrix.file_suffix }}
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||
|
||||
- name: Generate key pair
|
||||
run: cosign generate-key-pair
|
||||
|
||||
- name: Sign and generate attestations
|
||||
run: |
|
||||
cosign sign-blob \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-signature ./libs/Stirling-PDF${{ matrix.file_suffix }}.jar.sig \
|
||||
./libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
|
||||
cosign attest-blob \
|
||||
--predicate - \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-attestation ./libs/Stirling-PDF${{ matrix.file_suffix }}.jar.intoto.jsonl \
|
||||
./libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
|
||||
cosign verify-blob \
|
||||
--key ./cosign.pub \
|
||||
--signature ./libs/Stirling-PDF${{ matrix.file_suffix }}.jar.sig \
|
||||
./libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
|
||||
cosign sign-blob \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-signature ./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe.sig \
|
||||
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
|
||||
cosign attest-blob \
|
||||
--predicate - \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-attestation ./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe.intoto.jsonl \
|
||||
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
|
||||
cosign verify-blob \
|
||||
--key ./cosign.pub \
|
||||
--signature ./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe.sig \
|
||||
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
|
||||
- name: Upload signed artifacts
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: signed${{ matrix.file_suffix }}
|
||||
path: |
|
||||
./libs/Stirling-PDF${{ matrix.file_suffix }}.*
|
||||
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.*
|
||||
|
||||
release:
|
||||
needs: [build, sign_verify]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
matrix:
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- enable_security: true
|
||||
file_suffix: "-with-login"
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Download signed artifacts
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: signed${{ matrix.file_suffix }}
|
||||
|
||||
- name: Upload binaries, attestations and signatures to Release and create GitHub Release
|
||||
- name: Upload binaries to release
|
||||
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
||||
with:
|
||||
tag_name: v${{ needs.build.outputs.version }}
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
./libs/Stirling-PDF*
|
||||
./launch4j/Stirling-PDF-Server*
|
||||
files: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
|
||||
- name: Rename jar binaries
|
||||
run: cp ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
|
||||
- name: Upload Assets jar binaries
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
path: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
name: Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
overwrite: true
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload jar binaries to release
|
||||
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
||||
with:
|
||||
files: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
|
||||
2
.github/workflows/sync_files.yml
vendored
2
.github/workflows/sync_files.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Install dependencies
|
||||
run: pip install --require-hashes -r ./.github/scripts/requirements_sync_readme.txt
|
||||
run: pip install tomlkit
|
||||
- name: Sync README
|
||||
run: python scripts/counter_translation.py
|
||||
- name: Set up git config
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -14,16 +14,12 @@ local.properties
|
||||
.classpath
|
||||
.project
|
||||
version.properties
|
||||
|
||||
#### Stirling-PDF Files ###
|
||||
pipeline/watchedFolders/
|
||||
pipeline/finishedFolders/
|
||||
#### Stirling-PDF Files ###
|
||||
customFiles/
|
||||
configs/
|
||||
watchedFolders/
|
||||
!cucumber/
|
||||
!cucumber/exampleFiles/
|
||||
!cucumber/exampleFiles/example_html.zip
|
||||
|
||||
# Gradle
|
||||
.gradle
|
||||
@@ -115,7 +111,6 @@ watchedFolders/
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
*.db
|
||||
|
||||
@@ -20,7 +20,7 @@ repos:
|
||||
- --skip="./.*,*.csv,*.json,*.ambr"
|
||||
- --quiet-level=2
|
||||
files: \.(properties|html|css|js|py|md)$
|
||||
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile|.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js)
|
||||
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile)
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.22.0
|
||||
hooks:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Main stage
|
||||
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
|
||||
FROM alpine:3.20.3
|
||||
|
||||
# Copy necessary files
|
||||
COPY scripts /scripts
|
||||
|
||||
@@ -12,7 +12,7 @@ RUN DOCKER_ENABLE_SECURITY=true \
|
||||
./gradlew clean build
|
||||
|
||||
# Main stage
|
||||
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
|
||||
FROM alpine:3.20.3
|
||||
|
||||
# Copy necessary files
|
||||
COPY scripts /scripts
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# use alpine
|
||||
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
|
||||
FROM alpine:3.21.0@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ Stirling-PDF currently supports 38 languages!
|
||||
| German (Deutsch) (de_DE) |  |
|
||||
| Greek (Ελληνικά) (el_GR) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||
| Irish (Gaeilge) (ga_IE) |  |
|
||||
| Italian (Italiano) (it_IT) |  |
|
||||
@@ -142,7 +142,7 @@ Stirling-PDF currently supports 38 languages!
|
||||
| Persian (فارسی) (fa_IR) |  |
|
||||
| Polish (Polski) (pl_PL) |  |
|
||||
| Portuguese (Português) (pt_PT) |  |
|
||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||
| Romanian (Română) (ro_RO) |  |
|
||||
| Russian (Русский) (ru_RU) |  |
|
||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||
@@ -152,7 +152,7 @@ Stirling-PDF currently supports 38 languages!
|
||||
| Swedish (Svenska) (sv_SE) |  |
|
||||
| Thai (ไทย) (th_TH) |  |
|
||||
| Tibetan (བོད་ཡིག་) (zh_BO) |  |
|
||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||
| Turkish (Türkçe) (tr_TR) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||
|
||||
@@ -5,7 +5,7 @@ plugins {
|
||||
id "org.springdoc.openapi-gradle-plugin" version "1.8.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.1"
|
||||
id "com.diffplug.spotless" version "6.25.0"
|
||||
id "com.github.jk1.dependency-license-report" version "2.9"
|
||||
//id "nebula.lint" version "19.0.3"
|
||||
id("org.panteleyev.jpackageplugin") version "1.6.0"
|
||||
@@ -27,7 +27,7 @@ ext {
|
||||
}
|
||||
|
||||
group = "stirling.software"
|
||||
version = "0.37.0"
|
||||
version = "0.36.6"
|
||||
|
||||
|
||||
java {
|
||||
|
||||
@@ -8,3 +8,4 @@
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -18,4 +18,4 @@ def after_scenario(context, scenario):
|
||||
# Remove any temporary files
|
||||
for temp_file in os.listdir('.'):
|
||||
if temp_file.startswith('genericNonCustomisableName') or temp_file.startswith('temp_image_'):
|
||||
os.remove(temp_file)
|
||||
os.remove(temp_file)
|
||||
@@ -1,8 +1,7 @@
|
||||
import os
|
||||
import requests
|
||||
from behave import given, when, then
|
||||
from pypdf import PdfWriter, PdfReader
|
||||
from pypdf.errors import PdfReadError
|
||||
from PyPDF2 import PdfWriter, PdfReader
|
||||
import io
|
||||
import random
|
||||
import string
|
||||
@@ -43,7 +42,7 @@ def step_use_example_file(context, filePath, fileInput):
|
||||
context.file_name = filePath.split('/')[-1]
|
||||
if not hasattr(context, 'files'):
|
||||
context.files = {}
|
||||
|
||||
|
||||
# Ensure the file exists before opening
|
||||
try:
|
||||
example_file = open(filePath, 'rb')
|
||||
@@ -166,17 +165,17 @@ def step_pdf_contains_pages_with_random_text(context, page_count):
|
||||
buffer = io.BytesIO()
|
||||
c = canvas.Canvas(buffer, pagesize=letter)
|
||||
width, height = letter
|
||||
|
||||
|
||||
for _ in range(page_count):
|
||||
text = ''.join(random.choices(string.ascii_letters + string.digits, k=100))
|
||||
c.drawString(100, height - 100, text)
|
||||
c.showPage()
|
||||
|
||||
|
||||
c.save()
|
||||
|
||||
|
||||
with open(context.file_name, 'wb') as f:
|
||||
f.write(buffer.getvalue())
|
||||
|
||||
|
||||
context.files[context.param_name].close()
|
||||
context.files[context.param_name] = open(context.file_name, 'rb')
|
||||
|
||||
@@ -185,16 +184,16 @@ def step_pdf_pages_contain_text(context, text):
|
||||
buffer = io.BytesIO()
|
||||
c = canvas.Canvas(buffer, pagesize=letter)
|
||||
width, height = letter
|
||||
|
||||
|
||||
for _ in range(len(PdfReader(context.file_name).pages)):
|
||||
c.drawString(100, height - 100, text)
|
||||
c.showPage()
|
||||
|
||||
|
||||
c.save()
|
||||
|
||||
|
||||
with open(context.file_name, 'wb') as f:
|
||||
f.write(buffer.getvalue())
|
||||
|
||||
|
||||
context.files[context.param_name].close()
|
||||
context.files[context.param_name] = open(context.file_name, 'rb')
|
||||
|
||||
@@ -346,7 +345,7 @@ def step_check_response_pdf_page_count(context, page_count):
|
||||
def step_check_response_zip_file_count(context, file_count):
|
||||
response_file = io.BytesIO(context.response.content)
|
||||
with zipfile.ZipFile(io.BytesIO(response_file.getvalue())) as zip_file:
|
||||
actual_file_count = len(zip_file.namelist())
|
||||
actual_file_count = len(zip_file.namelist())
|
||||
assert actual_file_count == file_count, f"Expected {file_count} files but got {actual_file_count} files"
|
||||
|
||||
@then('the response ZIP file should contain {doc_count:d} documents each having {pages_per_doc:d} pages')
|
||||
@@ -355,7 +354,7 @@ def step_check_response_zip_doc_page_count(context, doc_count, pages_per_doc):
|
||||
with zipfile.ZipFile(io.BytesIO(response_file.getvalue())) as zip_file:
|
||||
actual_doc_count = len(zip_file.namelist())
|
||||
assert actual_doc_count == doc_count, f"Expected {doc_count} documents but got {actual_doc_count} documents"
|
||||
|
||||
|
||||
for file_name in zip_file.namelist():
|
||||
with zip_file.open(file_name) as pdf_file:
|
||||
reader = PdfReader(pdf_file)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
behave
|
||||
requests
|
||||
pypdf
|
||||
PyPDF2
|
||||
reportlab
|
||||
PyCryptodome
|
||||
|
||||
@@ -231,9 +231,9 @@ pycryptodome==3.21.0 \
|
||||
--hash=sha256:f7787e0d469bdae763b876174cf2e6c0f7be79808af26b1da96f1a64bcf47297 \
|
||||
--hash=sha256:ff99f952db3db2fbe98a0b355175f93ec334ba3d01bbde25ad3a5a33abc02b58
|
||||
# via -r cucumber\requirements.in
|
||||
pypdf==5.1.0 \
|
||||
--hash=sha256:3bd4f503f4ebc58bae40d81e81a9176c400cbbac2ba2d877367595fb524dfdfc \
|
||||
--hash=sha256:425a129abb1614183fd1aca6982f650b47f8026867c0ce7c4b9f281c443d2740
|
||||
pypdf2==3.0.1 \
|
||||
--hash=sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440 \
|
||||
--hash=sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928
|
||||
# via -r cucumber\requirements.in
|
||||
reportlab==4.2.5 \
|
||||
--hash=sha256:5cf35b8fd609b68080ac7bbb0ae1e376104f7d5f7b2d3914c7adc63f2593941f \
|
||||
@@ -249,10 +249,6 @@ six==1.17.0 \
|
||||
# via
|
||||
# behave
|
||||
# parse-type
|
||||
typing-extensions==4.12.2 \
|
||||
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
|
||||
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
|
||||
# via pypdf
|
||||
urllib3==2.3.0 \
|
||||
--hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \
|
||||
--hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Enables parallel execution of tasks, allowing multiple tasks to run simultaneously
|
||||
org.gradle.parallel=true
|
||||
|
||||
# Enables build caching to reuse outputs from previous builds for faster execution
|
||||
# org.gradle.caching=true
|
||||
|
||||
org.gradle.build-scan=true
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,7 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
37
gradlew
vendored
37
gradlew
vendored
@@ -15,8 +15,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -57,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -82,11 +80,13 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -133,29 +133,22 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -200,15 +193,11 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
||||
23
gradlew.bat
vendored
23
gradlew.bat
vendored
@@ -13,8 +13,6 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@@ -28,7 +26,6 @@ if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -45,11 +42,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -59,11 +56,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
@@ -22,13 +22,12 @@ public class InstallationPathConfig {
|
||||
// Pipeline paths
|
||||
private static final String PIPELINE_WATCHED_FOLDERS_PATH;
|
||||
private static final String PIPELINE_FINISHED_FOLDERS_PATH;
|
||||
private static final String PIPELINE_DEFAULT_WEB_UI_CONFIGS;
|
||||
|
||||
|
||||
// Custom file paths
|
||||
private static final String STATIC_PATH;
|
||||
private static final String TEMPLATES_PATH;
|
||||
private static final String SIGNATURES_PATH;
|
||||
|
||||
|
||||
static {
|
||||
BASE_PATH = initializeBasePath();
|
||||
|
||||
@@ -46,8 +45,7 @@ public class InstallationPathConfig {
|
||||
// Initialize pipeline paths
|
||||
PIPELINE_WATCHED_FOLDERS_PATH = PIPELINE_PATH + "watchedFolders" + File.separator;
|
||||
PIPELINE_FINISHED_FOLDERS_PATH = PIPELINE_PATH + "finishedFolders" + File.separator;
|
||||
PIPELINE_DEFAULT_WEB_UI_CONFIGS = PIPELINE_PATH + "defaultWebUIConfigs" + File.separator;
|
||||
|
||||
|
||||
// Initialize custom file paths
|
||||
STATIC_PATH = CUSTOM_FILES_PATH + "static" + File.separator;
|
||||
TEMPLATES_PATH = CUSTOM_FILES_PATH + "templates" + File.separator;
|
||||
@@ -120,10 +118,6 @@ public class InstallationPathConfig {
|
||||
return PIPELINE_FINISHED_FOLDERS_PATH;
|
||||
}
|
||||
|
||||
public static String getPipelineDefaultWebUIConfigsDir() {
|
||||
return PIPELINE_DEFAULT_WEB_UI_CONFIGS;
|
||||
}
|
||||
|
||||
public static String getStaticPath() {
|
||||
return STATIC_PATH;
|
||||
}
|
||||
|
||||
@@ -329,16 +329,12 @@ public class UserService implements UserServiceInterface {
|
||||
|
||||
public boolean isUsernameValid(String username) {
|
||||
// Checks whether the simple username is formatted correctly
|
||||
// Regular expression for user name: Min. 3 characters, max. 50 characters
|
||||
boolean isValidSimpleUsername =
|
||||
username.matches("^[a-zA-Z0-9](?!.*[-@._+]{2,})[a-zA-Z0-9@._+-]{1,48}[a-zA-Z0-9]$");
|
||||
|
||||
username.matches("^[a-zA-Z0-9][a-zA-Z0-9@._+-]*[a-zA-Z0-9]$");
|
||||
// Checks whether the email address is formatted correctly
|
||||
// Regular expression for email addresses: Max. 320 characters, with RFC-like validation
|
||||
boolean isValidEmail =
|
||||
username.matches(
|
||||
"^(?=.{1,320}$)(?=.{1,64}@)[A-Za-z0-9](?:[A-Za-z0-9_.+-]*[A-Za-z0-9])?@[^-][A-Za-z0-9-]+(?:\\\\.[A-Za-z0-9-]+)*(?:\\\\.[A-Za-z]{2,})$");
|
||||
|
||||
"^(?=.{1,64}@)[A-Za-z0-9]+(\\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$");
|
||||
List<String> notAllowedUserList = new ArrayList<>();
|
||||
notAllowedUserList.add("ALL_USERS".toLowerCase());
|
||||
boolean notAllowedUser = notAllowedUserList.contains(username.toLowerCase());
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package stirling.software.SPDF.config.security.database;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@@ -11,7 +9,6 @@ import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
|
||||
@@ -20,8 +17,8 @@ import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
@Configuration
|
||||
public class DatabaseConfig {
|
||||
|
||||
public final String DATASOURCE_DEFAULT_URL;
|
||||
|
||||
public static final String DATASOURCE_DEFAULT_URL =
|
||||
"jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE";
|
||||
public static final String DATASOURCE_URL_TEMPLATE = "jdbc:%s://%s:%4d/%s";
|
||||
public static final String DEFAULT_DRIVER = "org.h2.Driver";
|
||||
public static final String DEFAULT_USERNAME = "sa";
|
||||
@@ -33,7 +30,6 @@ public class DatabaseConfig {
|
||||
public DatabaseConfig(
|
||||
ApplicationProperties applicationProperties,
|
||||
@Qualifier("runningEE") boolean runningEE) {
|
||||
DATASOURCE_DEFAULT_URL = "jdbc:h2:file:" + InstallationPathConfig.getConfigPath() + File.separator + "stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE";
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.runningEE = runningEE;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.springframework.jdbc.datasource.init.ScriptException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.exception.BackupNotFoundException;
|
||||
@@ -38,14 +37,12 @@ public class DatabaseService implements DatabaseInterface {
|
||||
|
||||
public static final String BACKUP_PREFIX = "backup_";
|
||||
public static final String SQL_SUFFIX = ".sql";
|
||||
private final Path BACKUP_DIR;
|
||||
private static final String BACKUP_DIR = "configs/db/backup/";
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final DataSource dataSource;
|
||||
|
||||
public DatabaseService(ApplicationProperties applicationProperties, DataSource dataSource) {
|
||||
this.BACKUP_DIR =
|
||||
Paths.get(InstallationPathConfig.getConfigPath(), "db", "backup").normalize();
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
@@ -58,9 +55,9 @@ public class DatabaseService implements DatabaseInterface {
|
||||
*/
|
||||
@Override
|
||||
public boolean hasBackup() {
|
||||
createBackupDirectory();
|
||||
Path filePath = Paths.get(BACKUP_DIR);
|
||||
|
||||
if (Files.exists(BACKUP_DIR)) {
|
||||
if (Files.exists(filePath)) {
|
||||
return !getBackupList().isEmpty();
|
||||
}
|
||||
|
||||
@@ -77,11 +74,11 @@ public class DatabaseService implements DatabaseInterface {
|
||||
List<FileInfo> backupFiles = new ArrayList<>();
|
||||
|
||||
if (isH2Database()) {
|
||||
createBackupDirectory();
|
||||
Path backupPath = Paths.get(BACKUP_DIR);
|
||||
|
||||
try (DirectoryStream<Path> stream =
|
||||
Files.newDirectoryStream(
|
||||
BACKUP_DIR,
|
||||
backupPath,
|
||||
path ->
|
||||
path.getFileName().toString().startsWith(BACKUP_PREFIX)
|
||||
&& path.getFileName()
|
||||
@@ -113,17 +110,6 @@ public class DatabaseService implements DatabaseInterface {
|
||||
return backupFiles;
|
||||
}
|
||||
|
||||
private void createBackupDirectory() {
|
||||
if (!Files.exists(BACKUP_DIR)) {
|
||||
try {
|
||||
Files.createDirectories(BACKUP_DIR);
|
||||
log.debug("create backup directory: {}", BACKUP_DIR);
|
||||
} catch (IOException e) {
|
||||
log.error("Error create backup directory: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importDatabase() {
|
||||
if (!hasBackup()) throw new BackupNotFoundException("No backup scripts were found.");
|
||||
@@ -269,8 +255,7 @@ public class DatabaseService implements DatabaseInterface {
|
||||
* @return the <code>Path</code> object for the given file name
|
||||
*/
|
||||
public Path getBackupFilePath(String fileName) {
|
||||
createBackupDirectory();
|
||||
Path filePath = BACKUP_DIR.resolve(fileName).normalize();
|
||||
Path filePath = Paths.get(BACKUP_DIR, fileName).normalize();
|
||||
if (!filePath.startsWith(BACKUP_DIR)) {
|
||||
throw new SecurityException("Path traversal detected");
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ public class GeneralWebController {
|
||||
model.addAttribute("currentPage", "pipeline");
|
||||
List<String> pipelineConfigs = new ArrayList<>();
|
||||
List<Map<String, String>> pipelineConfigsWithNames = new ArrayList<>();
|
||||
if (new File(InstallationPathConfig.getPipelineDefaultWebUIConfigsDir()).exists()) {
|
||||
try (Stream<Path> paths = Files.walk(Paths.get(InstallationPathConfig.getPipelineDefaultWebUIConfigsDir()))) {
|
||||
if (new File("./pipeline/defaultWebUIConfigs/").exists()) {
|
||||
try (Stream<Path> paths = Files.walk(Paths.get("./pipeline/defaultWebUIConfigs/"))) {
|
||||
List<Path> jsonFiles =
|
||||
paths.filter(Files::isRegularFile)
|
||||
.filter(p -> p.toString().endsWith(".json"))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -82,7 +82,7 @@ pages=頁面
|
||||
loading=載入中...
|
||||
addToDoc=新增至文件
|
||||
reset=重設
|
||||
apply=套用
|
||||
apply=Apply
|
||||
|
||||
legal.privacy=隱私權政策
|
||||
legal.terms=使用條款
|
||||
@@ -249,7 +249,7 @@ database.backupCreated=資料庫備份成功
|
||||
database.fileNotFound=找不到檔案
|
||||
database.fileNullOrEmpty=檔案不得為空或空白
|
||||
database.failedImportFile=匯入檔案失敗
|
||||
database.notSupported=您的資料庫連線不支援此功能。
|
||||
database.notSupported=This function is not available for your database connection.
|
||||
|
||||
session.expired=您的工作階段已過期。請重新整理頁面並再試一次。
|
||||
session.refreshPage=重新整理頁面
|
||||
@@ -278,7 +278,7 @@ home.split.desc=將 PDF 分割為多個文件
|
||||
split.tags=頁面操作,劃分,多頁,剪下,伺服器端
|
||||
|
||||
home.rotate.title=旋轉
|
||||
home.rotate.desc=輕鬆旋轉您的 PDF。
|
||||
home.rotate.desc=輕鬆旋轉你的 PDF。
|
||||
rotate.tags=伺服器端
|
||||
|
||||
|
||||
@@ -300,24 +300,24 @@ home.addImage.desc=在 PDF 的指定位置新增圖片
|
||||
addImage.tags=img,jpg,圖片,照片
|
||||
|
||||
home.watermark.title=新增浮水印
|
||||
home.watermark.desc=在您的 PDF 檔案中新增自訂浮水印。
|
||||
home.watermark.desc=在你的 PDF 檔案中新增自訂浮水印。
|
||||
watermark.tags=文字,重複,標籤,自有,版權,商標,img,jpg,圖片,照片
|
||||
|
||||
home.permissions.title=修改權限
|
||||
home.permissions.desc=修改您的 PDF 檔案權限
|
||||
home.permissions.desc=修改你的 PDF 檔案權限
|
||||
permissions.tags=讀取,寫入,編輯,列印
|
||||
|
||||
|
||||
home.removePages.title=移除
|
||||
home.removePages.desc=從您的 PDF 檔案中刪除不需要的頁面。
|
||||
home.removePages.desc=從你的 PDF 檔案中刪除不需要的頁面。
|
||||
removePages.tags=移除頁面,刪除頁面
|
||||
|
||||
home.addPassword.title=新增密碼
|
||||
home.addPassword.desc=用密碼加密您的 PDF 檔案。
|
||||
home.addPassword.desc=用密碼加密你的 PDF 檔案。
|
||||
addPassword.tags=安全,安全性
|
||||
|
||||
home.removePassword.title=移除密碼
|
||||
home.removePassword.desc=從您的 PDF 檔案中移除密碼保護。
|
||||
home.removePassword.desc=從你的 PDF 檔案中移除密碼保護。
|
||||
removePassword.tags=安全,解密,安全性,取消密碼,刪除密碼
|
||||
|
||||
home.compressPdfs.title=壓縮
|
||||
@@ -476,9 +476,9 @@ home.autoRedact.title=自動塗黑
|
||||
home.autoRedact.desc=根據輸入的文字自動塗黑 PDF 中的文字
|
||||
autoRedact.tags=塗黑,隱藏,塗黑,黑色,標記,隱藏
|
||||
|
||||
home.redact.title=手動塗黑
|
||||
home.redact.desc=依據選取的文字、繪製的形狀和選取的頁面塗黑 PDF
|
||||
redact.tags=塗黑,隱藏,黑色標記,黑色,標記,隱藏,手動
|
||||
home.redact.title=Manual Redaction
|
||||
home.redact.desc=Redacts a PDF based on selected text, drawn shapes and/or selected page(s)
|
||||
redact.tags=Redact,Hide,black out,black,marker,hidden,manual
|
||||
|
||||
home.tableExtraxt.title=PDF 轉 CSV
|
||||
home.tableExtraxt.desc=從 PDF 中提取表格並將其轉換為 CSV
|
||||
@@ -556,21 +556,21 @@ login.header=登入
|
||||
login.signin=登入
|
||||
login.rememberme=記住我
|
||||
login.invalid=使用者名稱或密碼無效。
|
||||
login.locked=您的帳號已被鎖定。
|
||||
login.locked=你的帳戶已被鎖定。
|
||||
login.signinTitle=請登入
|
||||
login.ssoSignIn=透過 SSO 單一登入
|
||||
login.ssoSignIn=透過織網單一簽入
|
||||
login.oauth2AutoCreateDisabled=OAuth 2.0 自動建立使用者功能已停用
|
||||
login.oauth2AdminBlockedUser=目前不允許未註冊的使用者註冊或登入。請聯絡系統管理員。
|
||||
login.oauth2AdminBlockedUser=目前已封鎖非註冊使用者的註冊或登入。請聯絡系統管理員。
|
||||
login.oauth2RequestNotFound=找不到驗證請求
|
||||
login.oauth2InvalidUserInfoResponse=使用者資訊回應無效
|
||||
login.oauth2invalidRequest=請求無效
|
||||
login.oauth2InvalidUserInfoResponse=無效的使用者資訊回應
|
||||
login.oauth2invalidRequest=無效的回應
|
||||
login.oauth2AccessDenied=存取被拒
|
||||
login.oauth2InvalidTokenResponse=無效的權杖回應
|
||||
login.oauth2InvalidIdToken=無效的身分權杖
|
||||
login.relyingPartyRegistrationNotFound=找不到任何信賴方註冊紀錄
|
||||
login.oauth2InvalidIdToken=無效的識別權杖
|
||||
login.relyingPartyRegistrationNotFound=No relying party registration found
|
||||
login.userIsDisabled=使用者已停用,目前此使用者無法登入。請聯絡系統管理員。
|
||||
login.alreadyLoggedIn=您已經登入了
|
||||
login.alreadyLoggedIn2=部裝置。請先從這些裝置登出後再試一次。
|
||||
login.alreadyLoggedIn2=個裝置。請登出其他裝置後再試一次。
|
||||
login.toManySessions=您有太多使用中的工作階段
|
||||
|
||||
#auto-redact
|
||||
@@ -586,30 +586,30 @@ autoRedact.convertPDFToImageLabel=將 PDF 轉換為 PDF-影像(用於移除方
|
||||
autoRedact.submitButton=送出
|
||||
|
||||
#redact
|
||||
redact.title=手動塗黑
|
||||
redact.header=手動塗黑
|
||||
redact.submit=塗黑
|
||||
redact.textBasedRedaction=以文字為基礎的塗黑
|
||||
redact.pageBasedRedaction=以頁面為基礎的塗黑
|
||||
redact.convertPDFToImageLabel=將 PDF 轉換為 PDF 影像(用於移除黑框後的文字)
|
||||
redact.pageRedactionNumbers.title=頁面
|
||||
redact.pageRedactionNumbers.placeholder=(例如 1,2,8 或 4,7,12-16 或 2n-1)
|
||||
redact.redactionColor.title=塗黑顏色
|
||||
redact.export=匯出
|
||||
redact.upload=上傳
|
||||
redact.boxRedaction=框選區域塗黑
|
||||
redact.zoom=縮放
|
||||
redact.zoomIn=放大
|
||||
redact.zoomOut=縮小
|
||||
redact.nextPage=下一頁
|
||||
redact.previousPage=上一頁
|
||||
redact.toggleSidebar=切換側邊欄
|
||||
redact.showThumbnails=顯示縮圖
|
||||
redact.showDocumentOutline=顯示文件大綱(按兩下可展開/摺疊所有項目)
|
||||
redact.showAttatchments=顯示附件
|
||||
redact.showLayers=顯示圖層(按兩下可將所有圖層重設為預設狀態)
|
||||
redact.colourPicker=顏色選擇器
|
||||
redact.findCurrentOutlineItem=尋找目前的大綱項目
|
||||
redact.title=Manual Redaction
|
||||
redact.header=Manual Redaction
|
||||
redact.submit=Redact
|
||||
redact.textBasedRedaction=Text based Redaction
|
||||
redact.pageBasedRedaction=Page-based Redaction
|
||||
redact.convertPDFToImageLabel=Convert PDF to PDF-Image (Used to remove text behind the box)
|
||||
redact.pageRedactionNumbers.title=Pages
|
||||
redact.pageRedactionNumbers.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
||||
redact.redactionColor.title=Redaction Color
|
||||
redact.export=Export
|
||||
redact.upload=Upload
|
||||
redact.boxRedaction=Box draw redaction
|
||||
redact.zoom=Zoom
|
||||
redact.zoomIn=Zoom in
|
||||
redact.zoomOut=Zoom out
|
||||
redact.nextPage=Next Page
|
||||
redact.previousPage=Previous Page
|
||||
redact.toggleSidebar=Toggle Sidebar
|
||||
redact.showThumbnails=Show Thumbnails
|
||||
redact.showDocumentOutline=Show Document Outline (double-click to expand/collapse all items)
|
||||
redact.showAttatchments=Show Attachments
|
||||
redact.showLayers=Show Layers (double-click to reset all layers to the default state)
|
||||
redact.colourPicker=Colour Picker
|
||||
redact.findCurrentOutlineItem=Find current outline item
|
||||
|
||||
#showJS
|
||||
showJS.title=顯示 JavaScript
|
||||
@@ -778,15 +778,15 @@ scalePages.submit=送出
|
||||
|
||||
#certSign
|
||||
certSign.title=憑證簽章
|
||||
certSign.header=使用您的憑證簽章(進行中)
|
||||
certSign.header=使用你的憑證簽章(進行中)
|
||||
certSign.selectPDF=選擇要簽章的 PDF 檔案:
|
||||
certSign.jksNote=注意:如果您的證書類型未被列在下方,請使用 keytool 命令列工具將其轉換為 Java Keystore (.jks) 檔案格式,然後選擇下面的 .jks 檔案選項。
|
||||
certSign.selectKey=選擇您的私鑰檔案(PKCS#8 格式,副檔名可能是 .pem 或 .der):
|
||||
certSign.selectCert=選擇您的憑證檔案(X.509 格式,副檔名可能是 .pem 或 .der):
|
||||
certSign.selectP12=選擇您的 PKCS#12 金鑰庫檔案(副檔名可能是 .p12 或 .pfx)(選填,如果有提供,則它應該包含您的私鑰和憑證):
|
||||
certSign.selectJKS=選擇您的 Java Keystore 檔案 (副檔名可能是 .jks 或 .keystore):
|
||||
certSign.jksNote=注意:如果你的證書類型未被列在下方,請使用 keytool 命令列工具將其轉換為 Java Keystore (.jks) 檔案格式,然後選擇下面的 .jks 檔案選項。
|
||||
certSign.selectKey=選擇你的私鑰檔案(PKCS#8 格式,副檔名可能是 .pem 或 .der):
|
||||
certSign.selectCert=選擇你的憑證檔案(X.509 格式,副檔名可能是 .pem 或 .der):
|
||||
certSign.selectP12=選擇你的 PKCS#12 金鑰庫檔案(副檔名可能是 .p12 或 .pfx)(選填,如果有提供,則它應該包含你的私鑰和憑證):
|
||||
certSign.selectJKS=選擇你的 Java Keystore 檔案 (副檔名可能是 .jks 或 .keystore):
|
||||
certSign.certType=憑證類型
|
||||
certSign.password=輸入您的金鑰庫或私鑰密碼(如果有的話):
|
||||
certSign.password=輸入你的金鑰庫或私鑰密碼(如果有的話):
|
||||
certSign.showSig=顯示簽章
|
||||
certSign.reason=原因
|
||||
certSign.location=位置
|
||||
@@ -862,7 +862,7 @@ sign.first=第一頁
|
||||
sign.last=最後一頁
|
||||
sign.next=下一頁
|
||||
sign.previous=上一頁
|
||||
sign.maintainRatio=切換維持長寬比
|
||||
sign.maintainRatio=Toggle maintain aspect ratio
|
||||
|
||||
|
||||
#repair
|
||||
@@ -1319,8 +1319,8 @@ splitByChapters.submit=分割 PDF
|
||||
fileChooser.click=點選
|
||||
fileChooser.or=或
|
||||
fileChooser.dragAndDrop=拖放檔案
|
||||
fileChooser.dragAndDropPDF=拖放 PDF 檔案
|
||||
fileChooser.dragAndDropImage=拖放圖片檔案
|
||||
fileChooser.dragAndDropPDF=Drag & Drop PDF file
|
||||
fileChooser.dragAndDropImage=Drag & Drop Image file
|
||||
fileChooser.hoveredDragAndDrop=將檔案拖放至此
|
||||
|
||||
#release notes
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>rename</title>
|
||||
<symbol id="icon-rename" viewBox="0 0 512 512">
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Combined-Shape" fill="currentColor" transform="translate(42.666667, 64.000000)">
|
||||
<path d="M362.666667,1.42108547e-14 L362.666667,21.3333333 L320,21.333 L320,362.666 L362.666667,362.666667 L362.666667,384 L320,383.999 L320,384 L298.666667,384 L298.666,383.999 L256,384 L256,362.666667 L298.666,362.666 L298.666,21.333 L256,21.3333333 L256,1.42108547e-14 L362.666667,1.42108547e-14 Z M426.666667,64 L426.666667,320 L341.333333,320 L341.333333,277.333333 L384,277.333333 L384,106.666667 L341.333333,106.666667 L341.333333,64 L426.666667,64 Z M277.333333,64 L277.333333,320 L3.55271368e-14,320 L3.55271368e-14,64 L277.333333,64 Z M179.2,89.6 L149.333333,89.6 L149.333333,234.666667 C149.333333,248 148.5,256.333333 147.875,264.354167 L147.792993,265.422171 L147.792993,265.422171 L147.714003,266.48894 C147.417695,270.579012 147.2,274.696296 147.2,279.466667 L147.2,279.466667 L177.066667,279.466667 L177.066667,260.266667 C184.941497,273.926888 199.708077,282.130544 215.466667,281.6 C229.540046,281.805757 242.921593,275.508559 251.733333,264.533333 C263.162478,248.989677 269.832496,230.461848 270.933333,211.2 C270.933333,170.666667 249.6,142.933333 217.6,142.933333 C202.507405,142.999748 188.308689,150.099106 179.2,162.133333 L179.2,162.133333 L179.2,89.6 Z M119.466667,162.133333 C107.961824,149.843793 91.4322333,143.546807 74.6666667,145.066667 C57.6785115,144.485924 40.8138255,148.15216 25.6,155.733333 L25.6,155.733333 L34.1333333,177.066667 C45.3979052,171.147831 57.7246848,167.522308 70.4,166.4 C78.5613135,165.511423 86.6853595,168.371259 92.4903835,174.176283 C98.2954074,179.981307 101.155244,188.105353 100.266667,196.266667 L100.266667,196.266667 L100.266667,198.4 L78.9333333,198.4 C65.8181975,197.679203 52.705771,199.864608 40.5333333,204.8 C26.2806563,210.950309 17.6507691,225.621117 19.2,241.066667 C19.0625857,252.057651 23.6679763,262.574827 31.8381493,269.927982 C40.0083223,277.281138 50.9508304,280.757072 61.8666667,279.466667 C77.2795695,280.291768 92.2192911,274.001359 102.4,262.4 L102.4,262.4 L102.4,277.333333 L130.133333,277.333333 C128.292479,266.054406 127.577851,254.620365 128,243.2 L128,243.2 L128,204.8 C129.999138,190.023932 126.995128,175.003882 119.466667,162.133333 Z M98.1333333,213.333333 L98.1333333,238.933333 C92.082572,249.988391 80.836024,257.218314 68.2666667,258.133333 C63.0655139,258.520242 57.9538681,256.621996 54.2659359,252.934064 C50.5780036,249.246132 48.6797582,244.134486 49.0666667,238.933333 C49.0666667,224 59.7333333,215.466667 85.3333333,213.333333 L85.3333333,213.333333 L98.1333333,213.333333 Z M209.066667,166.4 C226.133333,166.4 238.933333,183.466667 238.933333,211.2 C238.933333,238.933333 228.266667,256 211.2,256 C197.298049,255.69869 184.825037,247.383349 179.2,234.666667 L179.2,234.666667 L179.2,187.733333 C185.154203,176.240507 196.263981,168.304951 209.066667,166.4 Z">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -1,10 +1,10 @@
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
|
||||
var cacheInputs = localStorage.getItem("cacheInputs") || "disabled";
|
||||
if (cacheInputs !== "enabled") {
|
||||
return; // Stop execution if caching is not enabled
|
||||
}
|
||||
|
||||
|
||||
// Function to generate a key based on the form's action attribute
|
||||
function generateStorageKey(form) {
|
||||
const action = form.getAttribute('action');
|
||||
@@ -24,11 +24,11 @@ document.addEventListener("DOMContentLoaded", function() {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const element = elements[i];
|
||||
// Skip elements without names, passwords, files, hidden fields, and submit/reset buttons
|
||||
if (!element.name ||
|
||||
element.type === 'password' ||
|
||||
element.type === 'file' ||
|
||||
//element.type === 'hidden' ||
|
||||
element.type === 'submit' ||
|
||||
if (!element.name ||
|
||||
element.type === 'password' ||
|
||||
element.type === 'file' ||
|
||||
//element.type === 'hidden' ||
|
||||
element.type === 'submit' ||
|
||||
element.type === 'reset') {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// Find all forms and add CSRF token
|
||||
const forms = document.querySelectorAll('form');
|
||||
const csrfToken = decodeCsrfToken(getCsrfToken());
|
||||
|
||||
|
||||
// Only proceed if we have a cookie-based token
|
||||
if (csrfToken) {
|
||||
forms.forEach(form => {
|
||||
@@ -34,4 +34,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
form.appendChild(csrfInput);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -102,4 +102,4 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
toggleDarkMode();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,7 @@ window.fetchWithCsrf = async function(url, options = {}) {
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('XSRF-TOKEN='))
|
||||
?.split('=')[1];
|
||||
|
||||
|
||||
if (cookieValue) {
|
||||
return cookieValue;
|
||||
}
|
||||
@@ -14,10 +14,10 @@ window.fetchWithCsrf = async function(url, options = {}) {
|
||||
|
||||
// Create a new options object to avoid modifying the passed object
|
||||
const fetchOptions = { ...options };
|
||||
|
||||
|
||||
// Ensure headers object exists
|
||||
fetchOptions.headers = { ...options.headers };
|
||||
|
||||
|
||||
// Add CSRF token if available
|
||||
const csrfToken = getCsrfToken();
|
||||
if (csrfToken) {
|
||||
@@ -25,4 +25,4 @@ window.fetchWithCsrf = async function(url, options = {}) {
|
||||
}
|
||||
|
||||
return fetch(url, fetchOptions);
|
||||
}
|
||||
}
|
||||
@@ -32,8 +32,8 @@ function initializeGame() {
|
||||
const BASE_SPAWN_INTERVAL_MS = 1250; // milliseconds before a new enemy spawns
|
||||
const LEVEL_INCREASE_FACTOR_MS = 25; // milliseconds to decrease the spawn interval per level
|
||||
const MAX_SPAWN_RATE_REDUCTION_MS = 800; // Max milliseconds from the base spawn interval
|
||||
|
||||
|
||||
|
||||
|
||||
let keysPressed = {};
|
||||
const pdfs = [];
|
||||
const projectiles = [];
|
||||
@@ -42,7 +42,7 @@ function initializeGame() {
|
||||
let pdfSpeed = BASE_PDF_SPEED;
|
||||
let gameOver = false;
|
||||
|
||||
|
||||
|
||||
function handleKeys() {
|
||||
if (keysPressed["ArrowLeft"]) {
|
||||
playerX -= PLAYER_MOVE_SPEED;
|
||||
@@ -72,7 +72,7 @@ function initializeGame() {
|
||||
function onKeyUp(event) {
|
||||
keysPressed[event.key] = false;
|
||||
}
|
||||
|
||||
|
||||
document.removeEventListener("keydown", onKeydown);
|
||||
document.removeEventListener("keyup", onKeyUp);
|
||||
document.addEventListener("keydown", onKeydown);
|
||||
@@ -243,7 +243,7 @@ function initializeGame() {
|
||||
|
||||
let spawnPdfTimeout;
|
||||
|
||||
|
||||
|
||||
|
||||
function spawnPdfInterval() {
|
||||
if (gameOver || paused) {
|
||||
|
||||
@@ -39,3 +39,4 @@ document.getElementById("cacheInputs").addEventListener("change", function () {
|
||||
cacheInputs = this.checked ? "enabled" : "disabled";
|
||||
localStorage.setItem("cacheInputs", cacheInputs);
|
||||
});
|
||||
|
||||
|
||||
@@ -104,14 +104,7 @@
|
||||
</div>
|
||||
<script th:inline="javascript">
|
||||
jQuery.validator.addMethod("usernamePattern", function(value, element) {
|
||||
// Regular expression for user name: Min. 3 characters, max. 50 characters
|
||||
const regexUsername = /^[a-zA-Z0-9](?!.*[-@._+]{2,})([a-zA-Z0-9@._+-]{1,48})[a-zA-Z0-9]$/;
|
||||
|
||||
// Regular expression for email addresses: Max. 320 characters, with RFC-like validation
|
||||
const regexEmail = /^(?=.{1,320}$)(?=.{1,64}@)[A-Za-z0-9](?:[A-Za-z0-9_.+-]*[A-Za-z0-9])?@[^-][A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*(?:\.[A-Za-z]{2,})$/;
|
||||
|
||||
// Check if the field is optional or meets the requirements
|
||||
return this.optional(element) || regexUsername.test(value) || regexEmail.test(value);
|
||||
return this.optional(element) || /^[a-zA-Z0-9][a-zA-Z0-9@._+-]*[a-zA-Z0-9]$|^(?=.{1,64}@)[A-Za-z0-9]+(\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.[A-Za-z]{2,})$/.test(value);
|
||||
}, /*[[#{invalidUsernameMessage}]]*/ "Invalid username format");
|
||||
$(document).ready(function() {
|
||||
$.validator.addMethod("passwordMatch", function(value, element) {
|
||||
@@ -315,36 +308,37 @@
|
||||
|
||||
document.getElementById('syncToAccount').addEventListener('click', async function() {
|
||||
/*<![CDATA[*/
|
||||
const urlUpdateUserSettings = /*[[@{/api/v1/user/updateUserSettings}]]*/ "/api/v1/user/updateUserSettings";
|
||||
/*]]>*/
|
||||
const urlUpdateUserSettings = /*[[@{/api/v1/user/updateUserSettings}]]*/ "/api/v1/user/updateUserSettings";
|
||||
/*]]>*/
|
||||
|
||||
let settings = {};
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if(key !== 'debug' && key !== '0' && key !== '1' && !key.includes('pdfjs') && !key.includes('posthog') && !key.includes('pageViews')) {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await window.fetchWithCsrf(urlUpdateUserSettings, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(settings)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Error syncing settings to account');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Error syncing settings to account');
|
||||
}
|
||||
});
|
||||
|
||||
let settings = {};
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if(key !== 'debug' && key !== '0' && key !== '1' && !key.includes('pdfjs') && !key.includes('posthog') && !key.includes('pageViews')) {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await window.fetchWithCsrf(urlUpdateUserSettings, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(settings)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Error syncing settings to account');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Error syncing settings to account');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div class="mb-3 mt-4 text-center">
|
||||
|
||||
@@ -207,14 +207,7 @@
|
||||
|
||||
<script th:inline="javascript">
|
||||
jQuery.validator.addMethod("usernamePattern", function(value, element) {
|
||||
// Regular expression for user name: Min. 3 characters, max. 50 characters
|
||||
const regexUsername = /^[a-zA-Z0-9](?!.*[-@._+]{2,})([a-zA-Z0-9@._+-]{1,48})[a-zA-Z0-9]$/;
|
||||
|
||||
// Regular expression for email addresses: Max. 320 characters, with RFC-like validation
|
||||
const regexEmail = /^(?=.{1,320}$)(?=.{1,64}@)[A-Za-z0-9](?:[A-Za-z0-9_.+-]*[A-Za-z0-9])?@[^-][A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*(?:\.[A-Za-z]{2,})$/;
|
||||
|
||||
// Check if the field is optional or meets the requirements
|
||||
return this.optional(element) || regexUsername.test(value) || regexEmail.test(value);
|
||||
return this.optional(element) || /^[a-zA-Z0-9][a-zA-Z0-9@._+-]*[a-zA-Z0-9]$|^(?=.{1,64}@)[A-Za-z0-9]+(\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.[A-Za-z]{2,})$/.test(value);
|
||||
}, /*[[#{invalidUsernameMessage}]]*/ "Invalid username format");
|
||||
$(document).ready(function() {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
<!-- powered by section -->
|
||||
<div class="footer-powered-by">
|
||||
<span th:text="#{poweredBy}"></span>
|
||||
<a href="https://stirlingpdf.com" class="stirling-link">Stirling PDF</a>
|
||||
</div>
|
||||
<span th:text="#{poweredBy}"></span>
|
||||
<a href="https://stirlingpdf.com" class="stirling-link">Stirling PDF</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -82,62 +82,61 @@
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-single-page', 'looks_one', 'home.PdfToSinglePage.title', 'home.PdfToSinglePage.desc', 'PdfToSinglePage.tags', 'organize')}">
|
||||
</div>
|
||||
</div>
|
||||
<div id="stacked" class="navbar-item col-lg-2 col-sm-6 py px-xl-1 px-2"
|
||||
style="display:flex; flex-direction: column;">
|
||||
<!-- Convert to PDF menu items -->
|
||||
<div class="navbar-item py px-xl-1 px-2" style="margin-bottom: 1rem;">
|
||||
<h6 class="menu-title" th:text="#{navbar.sections.convertTo}"></h6>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('img-to-pdf', 'picture_as_pdf', 'home.imageToPdf.title', 'home.imageToPdf.desc', 'imageToPdf.tags', 'image')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('file-to-pdf', 'draft', 'home.fileToPDF.title', 'home.fileToPDF.desc', 'fileToPDF.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('url-to-pdf', 'link', 'home.URLToPDF.title', 'home.URLToPDF.desc', 'URLToPDF.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('html-to-pdf', 'html', 'home.HTMLToPDF.title', 'home.HTMLToPDF.desc', 'HTMLToPDF.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('markdown-to-pdf', 'markdown', 'home.MarkdownToPDF.title', 'home.MarkdownToPDF.desc', 'MarkdownToPDF.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('book-to-pdf', 'book', 'home.BookToPDF.title', 'home.BookToPDF.desc', 'BookToPDF.tags', 'convert')}">
|
||||
</div>
|
||||
<div id="stacked" class="navbar-item col-lg-2 col-sm-6 py px-xl-1 px-2"style="display:flex; flex-direction: column;">
|
||||
<!-- Convert to PDF menu items -->
|
||||
<div class="navbar-item py px-xl-1 px-2" style="margin-bottom: 1rem;">
|
||||
<h6 class="menu-title" th:text="#{navbar.sections.convertTo}"></h6>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('img-to-pdf', 'image', 'home.imageToPdf.title', 'home.imageToPdf.desc', 'imageToPdf.tags', 'image')}">
|
||||
</div>
|
||||
<!-- Convert from PDF menu items -->
|
||||
<div class="navbar-item py px-xl-1">
|
||||
<h6 class="menu-title" th:text="#{navbar.sections.convertFrom}"></h6>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-img', 'photo_library', 'home.pdfToImage.title', 'home.pdfToImage.desc', 'pdfToImage.tags', 'image')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-word', 'description', 'home.PDFToWord.title', 'home.PDFToWord.desc', 'PDFToWord.tags', 'word')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-presentation', 'slideshow', 'home.PDFToPresentation.title', 'home.PDFToPresentation.desc', 'PDFToPresentation.tags', 'ppt')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-text', 'text_fields', 'home.PDFToText.title', 'home.PDFToText.desc', 'PDFToText.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-html', 'html', 'home.PDFToHTML.title', 'home.PDFToHTML.desc', 'PDFToHTML.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-xml', 'code', 'home.PDFToXML.title', 'home.PDFToXML.desc', 'PDFToXML.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-pdfa', 'picture_as_pdf', 'home.pdfToPDFA.title', 'home.pdfToPDFA.desc', 'pdfToPDFA.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-csv', 'csv', 'home.tableExtraxt.title', 'home.tableExtraxt.desc', 'pdfToPDFA.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-book', 'book', 'home.PDFToBook.title', 'home.PDFToBook.desc', 'PDFToBook.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('file-to-pdf', 'draft', 'home.fileToPDF.title', 'home.fileToPDF.desc', 'fileToPDF.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('url-to-pdf', 'link', 'home.URLToPDF.title', 'home.URLToPDF.desc', 'URLToPDF.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('html-to-pdf', 'html', 'home.HTMLToPDF.title', 'home.HTMLToPDF.desc', 'HTMLToPDF.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('markdown-to-pdf', 'markdown', 'home.MarkdownToPDF.title', 'home.MarkdownToPDF.desc', 'MarkdownToPDF.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('book-to-pdf', 'book', 'home.BookToPDF.title', 'home.BookToPDF.desc', 'BookToPDF.tags', 'convert')}">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Convert from PDF menu items -->
|
||||
<div class="navbar-item py px-xl-1">
|
||||
<h6 class="menu-title" th:text="#{navbar.sections.convertFrom}"></h6>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-img', 'image', 'home.pdfToImage.title', 'home.pdfToImage.desc', 'pdfToImage.tags', 'image')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-word', 'description', 'home.PDFToWord.title', 'home.PDFToWord.desc', 'PDFToWord.tags', 'word')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-presentation', 'slideshow', 'home.PDFToPresentation.title', 'home.PDFToPresentation.desc', 'PDFToPresentation.tags', 'ppt')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-text', 'text_fields', 'home.PDFToText.title', 'home.PDFToText.desc', 'PDFToText.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-html', 'html', 'home.PDFToHTML.title', 'home.PDFToHTML.desc', 'PDFToHTML.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-xml', 'code', 'home.PDFToXML.title', 'home.PDFToXML.desc', 'PDFToXML.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-pdfa', 'picture_as_pdf', 'home.pdfToPDFA.title', 'home.pdfToPDFA.desc', 'pdfToPDFA.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-csv', 'csv', 'home.tableExtraxt.title', 'home.tableExtraxt.desc', 'pdfToPDFA.tags', 'convert')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-book', 'book', 'home.PDFToBook.title', 'home.PDFToBook.desc', 'PDFToBook.tags', 'convert')}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Security menu items -->
|
||||
<div class="navbar-item col-lg-2 col-sm-6 py px-xl-1 px-2">
|
||||
<h6 class="menu-title" th:text="#{navbar.sections.security}"></h6>
|
||||
@@ -160,8 +159,8 @@
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('cert-sign', 'workspace_premium', 'home.certSign.title', 'home.certSign.desc', 'certSign.tags', 'security')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry('validate-signature','verified','home.validateSignature.title','home.validateSignature.desc','validateSignature.tags','security')}">
|
||||
</div>
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry('validate-signature','verified','home.validateSignature.title','home.validateSignature.desc','validateSignature.tags','security')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('remove-cert-sign', 'remove_moderator', 'home.removeCertSign.title', 'home.removeCertSign.desc', 'removeCertSign.tags', 'security')}">
|
||||
</div>
|
||||
@@ -169,10 +168,10 @@
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('sanitize-pdf', 'sanitizer', 'home.sanitizePdf.title', 'home.sanitizePdf.desc', 'sanitizePdf.tags', 'security')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('auto-redact', 'contract_delete', 'home.autoRedact.title', 'home.autoRedact.desc', 'autoRedact.tags', 'security')}">
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('auto-redact', 'ink_eraser', 'home.autoRedact.title', 'home.autoRedact.desc', 'autoRedact.tags', 'security')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('redact', 'playlist_remove', 'home.redact.title', 'home.redact.desc', 'redact.tags', 'security')}">
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('redact', 'ink_eraser', 'home.redact.title', 'home.redact.desc', 'redact.tags', 'security')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('stamp', 'approval', 'home.AddStampRequest.title', 'home.AddStampRequest.desc', 'AddStampRequest.tags', 'security')}">
|
||||
@@ -194,7 +193,7 @@
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('add-image', 'add_photo_alternate', 'home.addImage.title', 'home.addImage.desc', 'addImage.tags', 'other')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('extract-images', 'wallpaper', 'home.extractImages.title', 'home.extractImages.desc', 'extractImages.tags', 'other')}">
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('extract-images', 'photo_library', 'home.extractImages.title', 'home.extractImages.desc', 'extractImages.tags', 'other')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('flatten', 'layers_clear', 'home.flatten.title', 'home.flatten.desc', 'flatten.tags', 'other')}">
|
||||
@@ -231,7 +230,7 @@
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('pipeline', 'family_history', 'home.pipeline.title', 'home.pipeline.desc', 'pipeline.tags', 'advance')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntryCustom :: navbarEntry ('auto-rename', '/images/rename.svg#icon-rename', 'home.auto-rename.title', 'home.auto-rename.desc', 'auto-rename.tags', 'advance')}">
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('auto-rename', 'text_fields_alt', 'home.auto-rename.title', 'home.auto-rename.desc', 'auto-rename.tags', 'advance')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/navbarEntry :: navbarEntry ('repair', 'build', 'home.repair.title', 'home.repair.desc', 'repair.tags', 'advance')}">
|
||||
@@ -279,7 +278,7 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item" th:if="${@endpointConfiguration.isEndpointEnabled('pipeline')}">
|
||||
<li class="nav-item" th:if="${@endpointConfiguration.isEndpointEnabled('pipeline')}" >
|
||||
<a class="nav-link" href="#" th:href="@{'/pipeline'}"
|
||||
th:classappend="${currentPage}=='pipeline' ? 'active' : ''" th:title="#{home.pipeline.desc}">
|
||||
<span class="material-symbols-rounded">
|
||||
@@ -289,7 +288,7 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item" th:if="${@endpointConfiguration.isEndpointEnabled('compress-pdf')}">
|
||||
<li class="nav-item" th:if="${@endpointConfiguration.isEndpointEnabled('compress-pdf')}" >
|
||||
<a class="nav-link" href="#" title="#{home.compressPdfs.title}" th:href="@{'/compress-pdf'}"
|
||||
th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''" th:title="#{home.compressPdfs.desc}">
|
||||
<span class="material-symbols-rounded">
|
||||
@@ -331,18 +330,17 @@
|
||||
<span class="icon-text icon-hide" th:data-text="#{navbar.favorite}" th:text="#{navbar.favorite}"></span>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-tp dropdown-mw-28" aria-labelledby="navbarDropdown-5">
|
||||
<div class="dropdown-menu-wrapper px-xl-2 px-2" id="favoritesDropdown">
|
||||
<!-- Dropdown items will be added here by JavaScript -->
|
||||
</div>
|
||||
<div class="dropdown-menu-wrapper px-xl-2 px-2" id="favoritesDropdown" >
|
||||
<!-- Dropdown items will be added here by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="dark-mode-toggle" href="#" th:title="#{navbar.darkmode}">
|
||||
<span class="material-symbols-rounded" id="dark-mode-icon">
|
||||
dark_mode
|
||||
</span>
|
||||
<span class="icon-text icon-hide" id="dark-mode-text" th:data-text="#{navbar.darkmode}"
|
||||
th:text="#{navbar.darkmode}"></span>
|
||||
<span class="icon-text icon-hide" id="dark-mode-text" th:data-text="#{navbar.darkmode}" th:text="#{navbar.darkmode}"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
@@ -363,8 +361,7 @@
|
||||
</li>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link" href="#" id="searchDropdown" role="button" data-bs-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false" th:title="#{navbar.search}">
|
||||
<a class="nav-link" href="#" id="searchDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" th:title="#{navbar.search}">
|
||||
<span class="material-symbols-rounded">
|
||||
search
|
||||
</span>
|
||||
@@ -373,8 +370,7 @@
|
||||
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="searchDropdown">
|
||||
<div class="dropdown-menu-wrapper px-xl-2 px-2">
|
||||
<form th:action="@{''}" class="d-flex p-2 search-form" id="searchForm">
|
||||
<input class="form-control search-input" type="search" th:placeholder="#{navbar.search}"
|
||||
aria-label="Search" id="navbarSearchInput">
|
||||
<input class="form-control search-input" type="search" th:placeholder="#{navbar.search}" aria-label="Search" id="navbarSearchInput">
|
||||
</form>
|
||||
<!-- Search Results -->
|
||||
<div id="searchResults" class="search-results scrollable-y dropdown-mw-20"></div>
|
||||
@@ -383,15 +379,13 @@
|
||||
</li>
|
||||
|
||||
<li class="nav-item" th:if="${!@runningEE}">
|
||||
<a href="https://stirlingpdf.com/pricing" class="nav-link go-pro-link" target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
<a href="https://stirlingpdf.com/pricing" class="nav-link go-pro-link" target="_blank" rel="noopener noreferrer">
|
||||
<span class="go-pro-badge" th:text="#{enterpriseEdition.button}"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<!-- Settings Button -->
|
||||
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#settingsModal"
|
||||
th:title="#{navbar.settings}">
|
||||
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#settingsModal" th:title="#{navbar.settings}">
|
||||
<span class="material-symbols-rounded">
|
||||
settings
|
||||
</span>
|
||||
@@ -426,12 +420,10 @@
|
||||
th:title="#{visitGithub}">
|
||||
<img th:src="@{'/images/github.svg'}" alt="github">
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/stirlingtools/stirling-pdf" class="mx-1" role="button" target="_blank"
|
||||
th:title="#{seeDockerHub}">
|
||||
<a href="https://hub.docker.com/r/stirlingtools/stirling-pdf" class="mx-1" role="button" target="_blank"th:title="#{seeDockerHub}">
|
||||
<img th:src="@{'/images/docker.svg'}" alt="docker">
|
||||
</a>
|
||||
<a href="https://discord.gg/Cn8pWhQRxZ" class="mx-1" role="button" target="_blank"
|
||||
th:title="#{joinDiscord}">
|
||||
<a href="https://discord.gg/Cn8pWhQRxZ" class="mx-1" role="button" target="_blank" th:title="#{joinDiscord}">
|
||||
<img th:src="@{'/images/discord.svg'}" alt="discord">
|
||||
</a>
|
||||
</div>
|
||||
@@ -452,7 +444,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="zipThreshold" th:utext="#{settings.zipThreshold}"></label><br />
|
||||
<label for="zipThreshold" th:utext="#{settings.zipThreshold}"></label><br/>
|
||||
<input type="range" class="form-range" min="1" max="9" step="1" id="zipThreshold" value="4">
|
||||
<span id="zipThresholdValue" class="ms-2"></span>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<th:block th:fragment="navbarEntry(endpoint, toolIcon, titleKey, descKey, tagKey, toolGroup)"
|
||||
th:if="${@endpointConfiguration.isEndpointEnabled(endpoint)}">
|
||||
<a class="dropdown-item" href="#" th:href="@{${endpoint}}"
|
||||
th:classappend="${endpoint.equals(currentPage)} ? ${toolGroup} + ' active' : '' + ${toolGroup}"
|
||||
th:title="#{${descKey}}" th:data-bs-tags="#{${tagKey}}">
|
||||
<div class="icon" alt="icon" th:class="@{${toolGroup}}">
|
||||
<svg class="nav-icon" style="height: 2.5rem; width:2.5rem">
|
||||
<use th:xlink:href="@{${toolIcon}}"></use>
|
||||
</svg>
|
||||
<span class="icon-text" th:text="#{${titleKey}}"></span>
|
||||
</div>
|
||||
</a>
|
||||
</th:block>
|
||||
@@ -71,21 +71,21 @@
|
||||
|
||||
<div id="popularTools" class="feature-group">
|
||||
<div
|
||||
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.popular})}">
|
||||
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.popular})}">
|
||||
</div>
|
||||
<div class="feature-group-container">
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='view-pdf', cardTitle=#{home.viewPdf.title}, cardText=#{home.viewPdf.desc}, cardLink='view-pdf', toolIcon='menu_book', tags=#{viewPdf.tags}, toolGroup='other')}">
|
||||
th:replace="~{fragments/card :: card(id='view-pdf', cardTitle=#{home.viewPdf.title}, cardText=#{home.viewPdf.desc}, cardLink='view-pdf', toolIcon='menu_book', tags=#{viewPdf.tags}, toolGroup='other')}">
|
||||
</div>
|
||||
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='multi-tool', cardTitle=#{home.multiTool.title}, cardText=#{home.multiTool.desc}, cardLink='multi-tool', toolIcon='construction', tags=#{multiTool.tags}, toolGroup='organize')}">
|
||||
th:replace="~{fragments/card :: card(id='multi-tool', cardTitle=#{home.multiTool.title}, cardText=#{home.multiTool.desc}, cardLink='multi-tool', toolIcon='construction', tags=#{multiTool.tags}, toolGroup='organize')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='pipeline', cardTitle=#{home.pipeline.title}, cardText=#{home.pipeline.desc}, cardLink='pipeline', toolIcon='family_history', tags=#{pipeline.tags}, toolGroup='advance')}">
|
||||
th:replace="~{fragments/card :: card(id='pipeline', cardTitle=#{home.pipeline.title}, cardText=#{home.pipeline.desc}, cardLink='pipeline', toolIcon='family_history', tags=#{pipeline.tags}, toolGroup='advance')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='compress-pdf', cardTitle=#{home.compressPdfs.title}, cardText=#{home.compressPdfs.desc}, cardLink='compress-pdf', toolIcon='zoom_in_map', tags=#{compressPdfs.tags}, toolGroup='advance')}">
|
||||
th:replace="~{fragments/card :: card(id='compress-pdf', cardTitle=#{home.compressPdfs.title}, cardText=#{home.compressPdfs.desc}, cardLink='compress-pdf', toolIcon='zoom_in_map', tags=#{compressPdfs.tags}, toolGroup='advance')}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -99,7 +99,7 @@
|
||||
</div>
|
||||
<div class="feature-group-container">
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='multi-tool', cardTitle=#{home.multiTool.title}, cardText=#{home.multiTool.desc}, cardLink='multi-tool', toolIcon='construction', tags=#{multiTool.tags}, toolGroup='organize')}">
|
||||
th:replace="~{fragments/card :: card(id='multi-tool', cardTitle=#{home.multiTool.title}, cardText=#{home.multiTool.desc}, cardLink='multi-tool', toolIcon='construction', tags=#{multiTool.tags}, toolGroup='organize')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='merge-pdfs', cardTitle=#{home.merge.title}, cardText=#{home.merge.desc}, cardLink='merge-pdfs', toolIcon='add_to_photos', tags=#{merge.tags}, toolGroup='organize')}">
|
||||
@@ -140,7 +140,7 @@
|
||||
</div>
|
||||
<div class="feature-group-container">
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='img-to-pdf', cardTitle=#{home.imageToPdf.title}, cardText=#{home.imageToPdf.desc}, cardLink='picture_as_pdf', toolIcon='picture_as_pdf', tags=#{imageToPdf.tags}, toolGroup='image')}">
|
||||
th:replace="~{fragments/card :: card(id='img-to-pdf', cardTitle=#{home.imageToPdf.title}, cardText=#{home.imageToPdf.desc}, cardLink='img-to-pdf', toolIcon='image', tags=#{imageToPdf.tags}, toolGroup='image')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='file-to-pdf', cardTitle=#{home.fileToPDF.title}, cardText=#{home.fileToPDF.desc}, cardLink='file-to-pdf', toolIcon='draft', tags=#{fileToPDF.tags}, toolGroup='convert')}">
|
||||
@@ -166,7 +166,7 @@
|
||||
</div>
|
||||
<div class="feature-group-container">
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='pdf-to-img', cardTitle=#{home.pdfToImage.title}, cardText=#{home.pdfToImage.desc}, cardLink='pdf-to-img', toolIcon='photo_library', tags=#{pdfToImage.tags}, toolGroup='image')}">
|
||||
th:replace="~{fragments/card :: card(id='pdf-to-img', cardTitle=#{home.pdfToImage.title}, cardText=#{home.pdfToImage.desc}, cardLink='pdf-to-img', toolIcon='image', tags=#{pdfToImage.tags}, toolGroup='image')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='pdf-to-pdfa', cardTitle=#{home.pdfToPDFA.title}, cardText=#{home.pdfToPDFA.desc}, cardLink='pdf-to-pdfa', toolIcon='picture_as_pdf', tags=#{pdfToPDFA.tags}, toolGroup='convert')}">
|
||||
@@ -215,9 +215,9 @@
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='cert-sign', cardTitle=#{home.certSign.title}, cardText=#{home.certSign.desc}, cardLink='cert-sign', toolIcon='workspace_premium', tags=#{certSign.tags}, toolGroup='security')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='validate-signature', cardTitle=#{home.validateSignature.title}, cardText=#{home.validateSignature.desc}, cardLink='validate-signature', toolIcon='verified', tags=#{validateSignature.tags}, toolGroup='security')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='validate-signature', cardTitle=#{home.validateSignature.title}, cardText=#{home.validateSignature.desc}, cardLink='validate-signature', toolIcon='verified', tags=#{validateSignature.tags}, toolGroup='security')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='remove-cert-sign', cardTitle=#{home.removeCertSign.title}, cardText=#{home.removeCertSign.desc}, cardLink='remove-cert-sign', toolIcon='remove_moderator', tags=#{removeCertSign.tags}, toolGroup='security')}">
|
||||
</div>
|
||||
@@ -228,7 +228,7 @@
|
||||
th:replace="~{fragments/card :: card(id='auto-redact', cardTitle=#{home.autoRedact.title}, cardText=#{home.autoRedact.desc}, cardLink='auto-redact', toolIcon='ink_eraser', tags=#{autoRedact.tags}, toolGroup='security')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='redact', cardTitle=#{home.redact.title}, cardText=#{home.redact.desc}, cardLink='redact', toolIcon='playlist_remove', tags=#{redact.tags}, toolGroup='security')}">
|
||||
th:replace="~{fragments/card :: card(id='redact', cardTitle=#{home.redact.title}, cardText=#{home.redact.desc}, cardLink='redact', toolIcon='ink_eraser', tags=#{redact.tags}, toolGroup='security')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='stamp', cardTitle=#{home.AddStampRequest.title}, cardText=#{home.AddStampRequest.desc}, cardLink='stamp', toolIcon='approval', tags=#{AddStampRequest.tags}, toolGroup='security')}">
|
||||
@@ -250,7 +250,7 @@
|
||||
th:replace="~{fragments/card :: card(id='add-page-numbers', cardTitle=#{home.add-page-numbers.title}, cardText=#{home.add-page-numbers.desc}, cardLink='add-page-numbers', toolIcon='123', tags=#{add-page-numbers.tags}, toolGroup='other')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='add-image', cardTitle=#{home.addImage.title}, cardText=#{home.addImage.desc}, cardLink='add-image', toolIcon='add_photo_alternate', tags=#{addImage.tags}, toolGroup='other')}">
|
||||
th:replace="~{fragments/card :: card(id='add-image', cardTitle=#{home.addImage.title}, cardText=#{home.addImage.desc}, cardLink='add-image', toolIcon='text_fields', tags=#{addImage.tags}, toolGroup='other')}">
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -260,7 +260,7 @@
|
||||
th:replace="~{fragments/card :: card(id='ocr-pdf', cardTitle=#{home.ocr.title}, cardText=#{home.ocr.desc}, cardLink='ocr-pdf', toolIcon='quick_reference_all', tags=#{ocr.tags}, toolGroup='other')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='extract-images', cardTitle=#{home.extractImages.title}, cardText=#{home.extractImages.desc}, cardLink='extract-images', toolIcon='wallpaper', tags=#{extractImages.tags}, toolGroup='other')}">
|
||||
th:replace="~{fragments/card :: card(id='extract-images', cardTitle=#{home.extractImages.title}, cardText=#{home.extractImages.desc}, cardLink='extract-images', toolIcon='photo_library', tags=#{extractImages.tags}, toolGroup='other')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='flatten', cardTitle=#{home.flatten.title}, cardText=#{home.flatten.desc}, cardLink='flatten', toolIcon='layers_clear', tags=#{flatten.tags}, toolGroup='other')}">
|
||||
@@ -281,13 +281,14 @@
|
||||
th:replace="~{fragments/card :: card(id='remove-image-pdf', cardTitle=#{home.removeImagePdf.title}, cardText=#{home.removeImagePdf.desc}, cardLink='remove-image-pdf', toolIcon='remove_selection', tags=#{removeImagePdf.tags}, toolGroup='other')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='replace-color-pdf', cardTitle=#{home.replaceColorPdf.title}, cardText=#{home.replaceColorPdf.desc}, cardLink='replace-and-invert-color-pdf', toolIcon='format_color_fill', tags=#{replaceColorPdf.tags}, toolGroup='other')}">
|
||||
th:replace="~{fragments/card :: card(id='replace-color-pdf', cardTitle=#{home.replaceColorPdf.title}, cardText=#{home.replaceColorPdf.desc}, cardLink='replace-and-invert-color-pdf', toolIcon='format_color_fill', tags=#{replaceColorPdf.tags}, toolGroup='other')}">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="groupAdvanced" class="feature-group">
|
||||
<div
|
||||
<div id="groupAdvanced" class="feature-group">
|
||||
<div
|
||||
|
||||
th:replace="~{fragments/featureGroupHeader :: featureGroupHeader(groupTitle=#{navbar.sections.advance})}">
|
||||
</div>
|
||||
<div class="feature-group-container">
|
||||
@@ -301,7 +302,7 @@
|
||||
th:replace="~{fragments/card :: card(id='compress-pdf', cardTitle=#{home.compressPdfs.title}, cardText=#{home.compressPdfs.desc}, cardLink='compress-pdf', toolIcon='zoom_in_map', tags=#{compressPdfs.tags}, toolGroup='advance')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='extract-image-scans', cardTitle=#{home.ScannerImageSplit.title}, cardText=#{home.ScannerImageSplit.desc}, cardLink='extract-image-scans', toolIcon='scanner', tags=#{ScannerImageSplit.tags}, toolGroup='advance')}">
|
||||
th:replace="~{fragments/card :: card(id='extract-image-scans', cardTitle=#{home.ScannerImageSplit.title}, cardText=#{home.ScannerImageSplit.desc}, cardLink='extract-image-scans', toolIcon='scanner', tags=#{ScannerImageSplit.tags}, toolGroup='advance')}">
|
||||
</div>
|
||||
<div
|
||||
th:replace="~{fragments/card :: card(id='repair', cardTitle=#{home.repair.title}, cardText=#{home.repair.desc}, cardLink='repair', toolIcon='build', tags=#{repair.tags}, toolGroup='advance')}">
|
||||
@@ -337,8 +338,7 @@
|
||||
|
||||
|
||||
<!-- Survey Modal -->
|
||||
<div class="modal fade" id="surveyModal" tabindex="-1" role="dialog" aria-labelledby="surveyModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal fade" id="surveyModal" tabindex="-1" role="dialog" aria-labelledby="surveyModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@@ -346,16 +346,12 @@
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p><span th:text="#{survey.changes}">Stirling-PDF has changed since the last survey! To find out more please
|
||||
check our blog post here: </span><a href="https://www.stirlingpdf.com/blog/stirling-pdf-future"
|
||||
target="_blank"> Stirling PDF</a></p>
|
||||
<p><span th:text="#{survey.changes}">Stirling-PDF has changed since the last survey! To find out more please check our blog post here: </span><a href="https://www.stirlingpdf.com/blog/stirling-pdf-future" target="_blank"> Stirling PDF</a></p>
|
||||
|
||||
<p th:text="#{survey.changes2}">With these changes we are getting paid business support and funding</p>
|
||||
<p th:text="#{survey.please}">Please consider taking our survey!</p>
|
||||
<p th:text="#{survey.disabled}">Survey popup will be disabled in following updates but available at foot of
|
||||
page)</p>
|
||||
<a href="https://stirlingpdf.info/s/cm28y3niq000o56dv7liv8wsu" target="_blank" class="btn btn-primary"
|
||||
id="takeSurvey" th:text="#{survey.button}">Take Survey</a>
|
||||
<p th:text="#{survey.disabled}">Survey popup will be disabled in following updates but available at foot of page)</p>
|
||||
<a href="https://stirlingpdf.info/s/cm28y3niq000o56dv7liv8wsu" target="_blank" class="btn btn-primary" id="takeSurvey"th:text="#{survey.button}" >Take Survey</a>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="form-check mb-3">
|
||||
@@ -372,29 +368,22 @@
|
||||
|
||||
|
||||
<!-- Analytics Modal -->
|
||||
<div class="modal fade" id="analyticsModal" tabindex="-1" role="dialog" aria-labelledby="analyticsModalLabel"
|
||||
aria-hidden="true" th:if="${@analyticsPrompt}">
|
||||
<div class="modal fade" id="analyticsModal" tabindex="-1" role="dialog" aria-labelledby="analyticsModalLabel" aria-hidden="true" th:if="${@analyticsPrompt}">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="analyticsModalLabel" th:text="#{analytics.title}">Do you want make Stirling PDF
|
||||
better?</h5>
|
||||
<h5 class="modal-title" id="analyticsModalLabel" th:text="#{analytics.title}">Do you want make Stirling PDF better?</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p th:text="#{analytics.paragraph1}">Stirling PDF has opt in analytics to help us improve the product. We do
|
||||
not track any personal information or file contents.</p>
|
||||
<p th:text="#{analytics.paragraph2}">Please consider enabling analytics to help Stirling-PDF grow and to allow
|
||||
us to understand our users better.</p>
|
||||
<p th:text="#{analytics.settings}">You can change the settings for analytics in the config/settings.yml file
|
||||
</p>
|
||||
<p th:text="#{analytics.paragraph1}">Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.</p>
|
||||
<p th:text="#{analytics.paragraph2}">Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.</p>
|
||||
<p th:text="#{analytics.settings}">You can change the settings for analytics in the config/settings.yml file</p>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-between">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" onclick="setAnalytics(false)"
|
||||
th:text="#{analytics.disable}">Disable analytics</button>
|
||||
<button type="button" class="btn btn-primary" th:text="#{analytics.enable}"
|
||||
onclick="setAnalytics(true)">Enable analytics</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" onclick="setAnalytics(false)" th:text="#{analytics.disable}">Disable analytics</button>
|
||||
<button type="button" class="btn btn-primary" th:text="#{analytics.enable}" onclick="setAnalytics(true)">Enable analytics</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -407,7 +396,7 @@
|
||||
/*<![CDATA[*/
|
||||
const analyticsPromptBoolean = /*[[${@analyticsPrompt}]]*/ false;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (analyticsPromptBoolean) {
|
||||
const analyticsModal = new bootstrap.Modal(document.getElementById('analyticsModal'));
|
||||
analyticsModal.show();
|
||||
@@ -422,76 +411,76 @@
|
||||
},
|
||||
body: JSON.stringify(enabled)
|
||||
})
|
||||
.then(response => {
|
||||
if (response.status === 200) {
|
||||
console.log('Analytics setting updated successfully');
|
||||
bootstrap.Modal.getInstance(document.getElementById('analyticsModal')).hide();
|
||||
} else if (response.status === 208) {
|
||||
console.log('Analytics setting has already been set. Please edit /config/settings.yml to change it.', response);
|
||||
alert('Analytics setting has already been set. Please edit /config/settings.yml to change it.');
|
||||
} else {
|
||||
throw new Error('Unexpected response status: ' + response.status);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error updating analytics setting:', error);
|
||||
alert('An error occurred while updating the analytics setting. Please try again.');
|
||||
});
|
||||
.then(response => {
|
||||
if (response.status === 200) {
|
||||
console.log('Analytics setting updated successfully');
|
||||
bootstrap.Modal.getInstance(document.getElementById('analyticsModal')).hide();
|
||||
} else if (response.status === 208) {
|
||||
console.log('Analytics setting has already been set. Please edit /config/settings.yml to change it.', response);
|
||||
alert('Analytics setting has already been set. Please edit /config/settings.yml to change it.');
|
||||
} else {
|
||||
throw new Error('Unexpected response status: ' + response.status);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error updating analytics setting:', error);
|
||||
alert('An error occurred while updating the analytics setting. Please try again.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const surveyVersion = "2.0";
|
||||
const modal = new bootstrap.Modal(document.getElementById('surveyModal'));
|
||||
const dontShowAgain = document.getElementById('dontShowAgain');
|
||||
const takeSurveyButton = document.getElementById('takeSurvey');
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const surveyVersion = "2.0";
|
||||
const modal = new bootstrap.Modal(document.getElementById('surveyModal'));
|
||||
const dontShowAgain = document.getElementById('dontShowAgain');
|
||||
const takeSurveyButton = document.getElementById('takeSurvey');
|
||||
|
||||
const viewThresholds = [5, 10, 15, 22, 30, 50, 75, 100, 150, 200];
|
||||
const viewThresholds = [5, 10, 15, 22, 30, 50, 75, 100, 150, 200];
|
||||
|
||||
// Check if survey version changed and reset page views if it did
|
||||
const storedVersion = localStorage.getItem('surveyVersion');
|
||||
if (storedVersion && storedVersion !== surveyVersion) {
|
||||
// Check if survey version changed and reset page views if it did
|
||||
const storedVersion = localStorage.getItem('surveyVersion');
|
||||
if (storedVersion && storedVersion !== surveyVersion) {
|
||||
localStorage.setItem('pageViews', '0');
|
||||
}
|
||||
}
|
||||
|
||||
let pageViews = parseInt(localStorage.getItem('pageViews') || '0');
|
||||
let pageViews = parseInt(localStorage.getItem('pageViews') || '0');
|
||||
|
||||
pageViews++;
|
||||
localStorage.setItem('pageViews', pageViews.toString());
|
||||
pageViews++;
|
||||
localStorage.setItem('pageViews', pageViews.toString());
|
||||
|
||||
function shouldShowSurvey() {
|
||||
function shouldShowSurvey() {
|
||||
if (localStorage.getItem('dontShowSurvey') === 'true' ||
|
||||
localStorage.getItem('surveyTaken') === 'true') {
|
||||
return false;
|
||||
localStorage.getItem('surveyTaken') === 'true') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If survey version changed and we hit a threshold, show the survey
|
||||
if (localStorage.getItem('surveyVersion') !== surveyVersion &&
|
||||
viewThresholds.includes(pageViews)) {
|
||||
return true;
|
||||
viewThresholds.includes(pageViews)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return viewThresholds.includes(pageViews);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldShowSurvey()) {
|
||||
if (shouldShowSurvey()) {
|
||||
modal.show();
|
||||
}
|
||||
}
|
||||
|
||||
dontShowAgain.addEventListener('change', function () {
|
||||
dontShowAgain.addEventListener('change', function() {
|
||||
if (this.checked) {
|
||||
localStorage.setItem('dontShowSurvey', 'true');
|
||||
localStorage.setItem('surveyVersion', surveyVersion);
|
||||
localStorage.setItem('dontShowSurvey', 'true');
|
||||
localStorage.setItem('surveyVersion', surveyVersion);
|
||||
} else {
|
||||
localStorage.removeItem('dontShowSurvey');
|
||||
localStorage.removeItem('surveyVersion');
|
||||
localStorage.removeItem('dontShowSurvey');
|
||||
localStorage.removeItem('surveyVersion');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
takeSurveyButton.addEventListener('click', function () {
|
||||
takeSurveyButton.addEventListener('click', function() {
|
||||
localStorage.setItem('surveyTaken', 'true');
|
||||
localStorage.setItem('surveyVersion', surveyVersion);
|
||||
modal.hide();
|
||||
@@ -506,4 +495,4 @@
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -12,87 +12,87 @@
|
||||
<div class="container-flex">
|
||||
<main class="form-signin">
|
||||
<script th:inline="javascript">
|
||||
const redirectAttempts = parseInt(localStorage.getItem('ssoRedirectAttempts') || '0');
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const hasRedirectError = urlParams.has('error');
|
||||
const hasLogout = urlParams.has('logout');
|
||||
const hasMessage = urlParams.has('message');
|
||||
const MAX_REDIRECT_ATTEMPTS = 3;
|
||||
|
||||
document.addEventListener('modeChanged', function(e) {
|
||||
var mode = e.detail;
|
||||
|
||||
document.body.classList.remove("light-mode", "dark-mode", "rainbow-mode"); // remove all mode classes first
|
||||
|
||||
switch (mode) {
|
||||
case "on":
|
||||
document.body.classList.add("dark-mode");
|
||||
break;
|
||||
case "off":
|
||||
document.body.classList.add("light-mode");
|
||||
break;
|
||||
case "rainbow":
|
||||
document.body.classList.add("rainbow-mode");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
const runningEE = /*[[${@runningEE}]]*/ false;
|
||||
const SSOAutoLogin = /*[[${@SSOAutoLogin}]]*/ false;
|
||||
const loginMethod = /*[[${loginMethod}]]*/ 'normal';
|
||||
const providerList = /*[[${providerlist}]]*/ {};
|
||||
const shouldAutoRedirect = !hasRedirectError &&
|
||||
!hasLogout &&
|
||||
!hasMessage &&
|
||||
redirectAttempts < MAX_REDIRECT_ATTEMPTS &&
|
||||
loginMethod !== 'normal' && runningEE && SSOAutoLogin;
|
||||
|
||||
console.log('Should redirect:', shouldAutoRedirect, {
|
||||
'No error': !hasRedirectError,
|
||||
'No logout': !hasLogout,
|
||||
'No message': !hasMessage,
|
||||
'Under max attempts': redirectAttempts < MAX_REDIRECT_ATTEMPTS,
|
||||
'Is only OAuth2': loginMethod === 'oauth2'
|
||||
});
|
||||
|
||||
if (shouldAutoRedirect && providerList && Object.keys(providerList).length > 0) {
|
||||
localStorage.setItem('ssoRedirectAttempts', redirectAttempts + 1);
|
||||
const firstProvider = Object.keys(providerList)[0];
|
||||
window.location.href = firstProvider;
|
||||
}
|
||||
|
||||
// Reset redirect attempts if successful login or after 1 hour
|
||||
const lastAttemptTime = parseInt(localStorage.getItem('lastRedirectAttempt') || '0');
|
||||
if (Date.now() - lastAttemptTime > 3600000) { // 1 hour
|
||||
localStorage.setItem('ssoRedirectAttempts', '0');
|
||||
}
|
||||
localStorage.setItem('lastRedirectAttempt', Date.now().toString());
|
||||
|
||||
|
||||
const defaultLocale = getStoredOrDefaultLocale();
|
||||
checkUserLanguage(defaultLocale);
|
||||
|
||||
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
|
||||
let activeItem;
|
||||
|
||||
for (let i = 0; i < dropdownItems.length; i++) {
|
||||
const item = dropdownItems[i];
|
||||
item.classList.remove('active');
|
||||
if (item.dataset.bsLanguageCode === defaultLocale) {
|
||||
item.classList.add('active');
|
||||
activeItem = item;
|
||||
}
|
||||
item.addEventListener('click', handleDropdownItemClick);
|
||||
}
|
||||
|
||||
const dropdown = document.getElementById('languageDropdown');
|
||||
|
||||
if (activeItem) {
|
||||
dropdown.innerHTML = activeItem.innerHTML; // This will set the dropdown button's content to the active language's flag and name
|
||||
}
|
||||
});
|
||||
const redirectAttempts = parseInt(localStorage.getItem('ssoRedirectAttempts') || '0');
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const hasRedirectError = urlParams.has('error');
|
||||
const hasLogout = urlParams.has('logout');
|
||||
const hasMessage = urlParams.has('message');
|
||||
const MAX_REDIRECT_ATTEMPTS = 3;
|
||||
|
||||
document.addEventListener('modeChanged', function(e) {
|
||||
var mode = e.detail;
|
||||
|
||||
document.body.classList.remove("light-mode", "dark-mode", "rainbow-mode"); // remove all mode classes first
|
||||
|
||||
switch (mode) {
|
||||
case "on":
|
||||
document.body.classList.add("dark-mode");
|
||||
break;
|
||||
case "off":
|
||||
document.body.classList.add("light-mode");
|
||||
break;
|
||||
case "rainbow":
|
||||
document.body.classList.add("rainbow-mode");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
const runningEE = [[${@runningEE}]];
|
||||
const SSOAutoLogin = [[${@SSOAutoLogin}]];
|
||||
const loginMethod = [[${loginMethod}]];
|
||||
const providerList = [[${providerlist}]];
|
||||
const shouldAutoRedirect = !hasRedirectError &&
|
||||
!hasLogout &&
|
||||
!hasMessage &&
|
||||
redirectAttempts < MAX_REDIRECT_ATTEMPTS &&
|
||||
loginMethod !== 'normal' && runningEE && SSOAutoLogin;
|
||||
|
||||
console.log('Should redirect:', shouldAutoRedirect, {
|
||||
'No error': !hasRedirectError,
|
||||
'No logout': !hasLogout,
|
||||
'No message': !hasMessage,
|
||||
'Under max attempts': redirectAttempts < MAX_REDIRECT_ATTEMPTS,
|
||||
'Is OAuth2': loginMethod === 'oauth2'
|
||||
});
|
||||
|
||||
if (shouldAutoRedirect && providerList && Object.keys(providerList).length > 0) {
|
||||
localStorage.setItem('ssoRedirectAttempts', redirectAttempts + 1);
|
||||
const firstProvider = Object.keys(providerList)[0];
|
||||
window.location.href = firstProvider;
|
||||
}
|
||||
|
||||
// Reset redirect attempts if successful login or after 1 hour
|
||||
const lastAttemptTime = parseInt(localStorage.getItem('lastRedirectAttempt') || '0');
|
||||
if (Date.now() - lastAttemptTime > 3600000) { // 1 hour
|
||||
localStorage.setItem('ssoRedirectAttempts', '0');
|
||||
}
|
||||
localStorage.setItem('lastRedirectAttempt', Date.now().toString());
|
||||
|
||||
|
||||
const defaultLocale = getStoredOrDefaultLocale();
|
||||
checkUserLanguage(defaultLocale);
|
||||
|
||||
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
|
||||
let activeItem;
|
||||
|
||||
for (let i = 0; i < dropdownItems.length; i++) {
|
||||
const item = dropdownItems[i];
|
||||
item.classList.remove('active');
|
||||
if (item.dataset.bsLanguageCode === defaultLocale) {
|
||||
item.classList.add('active');
|
||||
activeItem = item;
|
||||
}
|
||||
item.addEventListener('click', handleDropdownItemClick);
|
||||
}
|
||||
|
||||
const dropdown = document.getElementById('languageDropdown');
|
||||
|
||||
if (activeItem) {
|
||||
dropdown.innerHTML = activeItem.innerHTML; // This will set the dropdown button's content to the active language's flag and name
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<div class="text-center">
|
||||
<img class="my-4" th:src="@{'/favicon.svg'}" alt="favicon" width="144" height="144">
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
<span class="material-symbols-rounded tool-header-icon history">update</span>
|
||||
<span class="tool-header-text" th:text="#{releases.header}">Release Notes</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="alert alert-info" role="alert">
|
||||
<strong th:text="#{releases.current.version}">Current Installed Version</strong>:
|
||||
<strong th:text="#{releases.current.version}">Current Installed Version</strong>:
|
||||
<span id="currentVersion" th:text="${@appVersion}"></span>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<span th:text="#{releases.note}">All release notes are only available in English</span>
|
||||
</div>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<span th:text="#{releases.note}">All release notes are only available in English</span>
|
||||
</div>
|
||||
|
||||
<div id="loading" class="text-center my-4">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
@@ -51,42 +51,42 @@
|
||||
.release-notes-container {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
|
||||
.release-card {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.release-card.current-version {
|
||||
border-color: #28a745;
|
||||
background-color: rgba(40, 167, 69, 0.05);
|
||||
}
|
||||
|
||||
|
||||
.release-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.release-header h3 {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.version {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.release-date {
|
||||
color: #6c757d;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
|
||||
.release-body {
|
||||
font-size: 0.9rem;
|
||||
white-space: pre-wrap;
|
||||
@@ -105,17 +105,17 @@
|
||||
|
||||
<script th:inline="javascript">
|
||||
/*<![CDATA[*/
|
||||
|
||||
|
||||
// Get the current version from the appVersion bean
|
||||
const appVersion = /*[[${@appVersion}]]*/ '';
|
||||
|
||||
const appVersion = [[${@appVersion}]];
|
||||
|
||||
// GitHub API configuration
|
||||
const REPO_OWNER = 'Stirling-Tools';
|
||||
const REPO_NAME = 'Stirling-PDF';
|
||||
const GITHUB_API = 'https://api.github.com/repos/' + REPO_OWNER + '/' + REPO_NAME;
|
||||
const GITHUB_URL = 'https://github.com/' + REPO_OWNER + '/' + REPO_NAME;
|
||||
const MAX_RELEASES = 8;
|
||||
|
||||
const GITHUB_URL = 'https://github.com/' + REPO_OWNER + '/' + REPO_NAME;
|
||||
const MAX_RELEASES = 8;
|
||||
|
||||
// Secure element creation helper
|
||||
function createElement(tag, attributes = {}, children = []) {
|
||||
const element = document.createElement(tag);
|
||||
@@ -134,6 +134,7 @@
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
const ALLOWED_TAGS = {
|
||||
'a': ['href', 'target', 'rel', 'class'],
|
||||
'img': ['src', 'alt', 'width', 'height', 'style'],
|
||||
@@ -148,7 +149,7 @@
|
||||
try {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(htmlString, 'text/html');
|
||||
|
||||
|
||||
function sanitizeNode(node) {
|
||||
// Safety check for null/undefined
|
||||
if (!node) return null;
|
||||
@@ -161,7 +162,7 @@
|
||||
// Handle element nodes
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
const tagName = node.tagName.toLowerCase();
|
||||
|
||||
|
||||
// Check if tag is allowed
|
||||
if (!ALLOWED_TAGS[tagName]) {
|
||||
return document.createTextNode(node.textContent);
|
||||
@@ -169,7 +170,7 @@
|
||||
|
||||
// Create new element
|
||||
const cleanElement = document.createElement(tagName);
|
||||
|
||||
|
||||
// Copy allowed attributes
|
||||
const allowedAttributes = ALLOWED_TAGS[tagName];
|
||||
Array.from(node.attributes).forEach(attr => {
|
||||
@@ -243,7 +244,7 @@
|
||||
let lastIndex = 0;
|
||||
const result = document.createElement('span');
|
||||
const urlRegex = new RegExp('https://github\\.com/' + REPO_OWNER + '/' + REPO_NAME + '/(?:issues|pull)/(\\d+)', 'g');
|
||||
|
||||
|
||||
while ((match = urlRegex.exec(text)) !== null) {
|
||||
// Add text before the match
|
||||
if (match.index > lastIndex) {
|
||||
@@ -258,7 +259,7 @@
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener noreferrer';
|
||||
result.appendChild(link);
|
||||
|
||||
|
||||
lastIndex = match.index + match[0].length;
|
||||
}
|
||||
|
||||
@@ -273,37 +274,15 @@
|
||||
// Update formatText function to handle processGitHubReferences properly
|
||||
function formatText(text) {
|
||||
const container = document.createElement('div');
|
||||
|
||||
if (!text || typeof text !== 'string') {
|
||||
console.error('Invalid input to formatText:', text);
|
||||
return container;
|
||||
}
|
||||
|
||||
let textWithoutComments;
|
||||
try {
|
||||
textWithoutComments = text.replace(
|
||||
/<!-- Release notes generated using configuration in .github\/release\.yml at main -->/,
|
||||
''
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error in replace operation:', error);
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// Split the text into lines
|
||||
let lines;
|
||||
try {
|
||||
lines = textWithoutComments.split('\n');
|
||||
} catch (error) {
|
||||
console.error('Error in split operation:', error);
|
||||
return container;
|
||||
}
|
||||
|
||||
const textWithoutComments = text.replace(/<!--[\s\S]*?-->/g, '');
|
||||
const lines = textWithoutComments.split('\n');
|
||||
let currentList = null;
|
||||
|
||||
|
||||
lines.forEach(line => {
|
||||
const trimmedLine = line.trim();
|
||||
|
||||
|
||||
// Skip empty lines but add spacing
|
||||
if (!trimmedLine) {
|
||||
if (currentList) {
|
||||
@@ -313,14 +292,14 @@ function formatText(text) {
|
||||
container.appendChild(document.createElement('br'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check if the line is HTML
|
||||
if (trimmedLine.startsWith('<') && trimmedLine.endsWith('>')) {
|
||||
if (currentList) {
|
||||
container.appendChild(currentList);
|
||||
currentList = null;
|
||||
}
|
||||
|
||||
|
||||
const safeElement = createSafeElement(trimmedLine);
|
||||
if (safeElement) {
|
||||
container.appendChild(safeElement);
|
||||
@@ -330,7 +309,7 @@ function formatText(text) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check for headers
|
||||
const headerMatch = trimmedLine.match(/^(#{1,3})\s+(.+)$/);
|
||||
if (headerMatch) {
|
||||
@@ -347,40 +326,40 @@ function formatText(text) {
|
||||
container.appendChild(header);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check for bullet points
|
||||
const bulletMatch = trimmedLine.match(/^[-*]\s+(.+)$/);
|
||||
if (bulletMatch) {
|
||||
if (!currentList) {
|
||||
currentList = document.createElement('ul');
|
||||
}
|
||||
|
||||
|
||||
const listContent = bulletMatch[1];
|
||||
const listItem = document.createElement('li');
|
||||
|
||||
|
||||
// Process GitHub references in list items
|
||||
listItem.appendChild(processGitHubReferences(listContent));
|
||||
currentList.appendChild(listItem);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// If we reach here and have a list, append it
|
||||
if (currentList) {
|
||||
container.appendChild(currentList);
|
||||
currentList = null;
|
||||
}
|
||||
|
||||
|
||||
// Handle regular paragraph
|
||||
const paragraph = document.createElement('p');
|
||||
paragraph.appendChild(processGitHubReferences(trimmedLine));
|
||||
container.appendChild(paragraph);
|
||||
});
|
||||
|
||||
|
||||
// Append any remaining list
|
||||
if (currentList) {
|
||||
container.appendChild(currentList);
|
||||
}
|
||||
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@@ -389,7 +368,7 @@ function compareVersions(v1, v2) {
|
||||
const normalize = v => v.replace(/^v/, '');
|
||||
const v1Parts = normalize(v1).split('.').map(Number);
|
||||
const v2Parts = normalize(v2).split('.').map(Number);
|
||||
|
||||
|
||||
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
||||
const v1Part = v1Parts[i] || 0;
|
||||
const v2Part = v2Parts[i] || 0;
|
||||
@@ -407,38 +386,22 @@ async function loadReleases() {
|
||||
try {
|
||||
loading.classList.remove('d-none');
|
||||
errorMessage.classList.add('d-none');
|
||||
|
||||
|
||||
// Clear container safely
|
||||
while (container.firstChild) {
|
||||
container.removeChild(container.firstChild);
|
||||
}
|
||||
const cachedReleases = sessionStorage.getItem('releases');
|
||||
|
||||
let releases;
|
||||
if (cachedReleases) {
|
||||
releases = JSON.parse(cachedReleases);
|
||||
console.log("Read from storage");
|
||||
} else {
|
||||
const response = await fetch(GITHUB_API + '/releases');
|
||||
if (!response.ok) {
|
||||
if (response.status === 403) {
|
||||
throw new Error('API rate limit exceeded');
|
||||
}
|
||||
if (response.status === 404) {
|
||||
throw new Error('Repository not found');
|
||||
}
|
||||
throw new Error('Failed to fetch releases');
|
||||
}
|
||||
releases = await response.json();
|
||||
sessionStorage.setItem('releases', JSON.stringify(releases));
|
||||
}
|
||||
const response = await fetch(GITHUB_API + '/releases');
|
||||
if (!response.ok) throw new Error('Failed to fetch releases');
|
||||
const releases = await response.json();
|
||||
|
||||
// Sort releases by version number (descending)
|
||||
releases.sort((a, b) => compareVersions(b.tag_name, a.tag_name));
|
||||
|
||||
// Find index of current version
|
||||
const currentVersionIndex = releases.findIndex(release =>
|
||||
compareVersions(release.tag_name, 'v' + appVersion) === 0 ||
|
||||
const currentVersionIndex = releases.findIndex(release =>
|
||||
compareVersions(release.tag_name, 'v' + appVersion) === 0 ||
|
||||
compareVersions(release.tag_name, appVersion) === 0
|
||||
);
|
||||
|
||||
@@ -462,20 +425,20 @@ async function loadReleases() {
|
||||
|
||||
relevantReleases.forEach((release, index) => {
|
||||
const isCurrentVersion = index === 0; // First release in the array is current version
|
||||
|
||||
|
||||
const releaseCard = createElement('div', {
|
||||
class: `release-card ${isCurrentVersion ? 'current-version' : ''}`
|
||||
});
|
||||
|
||||
const header = createElement('div', { class: 'release-header' });
|
||||
|
||||
|
||||
const h3 = createElement('h3', {}, [
|
||||
createElement('span', { class: 'version' }, [release.tag_name]),
|
||||
createElement('span', { class: 'release-date' }, [
|
||||
new Date(release.created_at).toLocaleDateString()
|
||||
])
|
||||
]);
|
||||
|
||||
|
||||
header.appendChild(h3);
|
||||
|
||||
if (isCurrentVersion) {
|
||||
@@ -488,7 +451,7 @@ async function loadReleases() {
|
||||
|
||||
const body = createElement('div', { class: 'release-body' });
|
||||
body.appendChild(formatText(release.body || 'No release notes available.'));
|
||||
|
||||
|
||||
releaseCard.appendChild(body);
|
||||
container.appendChild(releaseCard);
|
||||
});
|
||||
@@ -504,8 +467,8 @@ async function loadReleases() {
|
||||
|
||||
// Load releases when the page loads
|
||||
document.addEventListener('DOMContentLoaded', loadReleases);
|
||||
|
||||
|
||||
/*]]>*/
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -21,9 +21,9 @@
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf')}"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label th:text="#{validateSignature.selectCustomCert}" ></label>
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='certFile', multipleInputsForSingleRequest=false, notRequired=true, remoteCall='false', accept='.cer,.crt,.pem')}"></div>
|
||||
</div>
|
||||
<label th:text="#{validateSignature.selectCustomCert}" ></label>
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='certFile', multipleInputsForSingleRequest=false, notRequired=true, remoteCall='false', accept='.cer,.crt,.pem')}"></div>
|
||||
</div>
|
||||
<div class="mb-3 text-left">
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{validateSignature.submit}"></button>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,7 @@ public class SPDFApplicationTest {
|
||||
|
||||
@Mock
|
||||
private ApplicationProperties applicationProperties;
|
||||
|
||||
|
||||
@InjectMocks
|
||||
private SPDFApplication SPDFApplication;
|
||||
|
||||
|
||||
@@ -103,4 +103,4 @@ class DatabaseConfigTest {
|
||||
|
||||
assertThrows(UnsupportedProviderException.class, () -> databaseConfig.dataSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,4 +98,4 @@ class RearrangePagesPDFControllerTest {
|
||||
assertNotNull(newPageOrder, "Returning null instead of page order list");
|
||||
assertEquals(Arrays.stream(expectedPageOrder.split(",")).map(Integer::parseInt).toList(), newPageOrder, "Page order doesn't match");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user