Compare commits

..

3 Commits

Author SHA1 Message Date
Anthony Stirling
f4082e3f96 Update PasswordController.java 2024-07-07 22:51:59 +01:00
Anthony Stirling
c93a48b40d Merge branch 'main' into decrypt 2024-07-07 22:50:41 +01:00
Anthony Stirling
75e10efcbd auto decrypt, update discord, fix multi file support for some inputs 2024-07-07 22:49:21 +01:00
142 changed files with 1278 additions and 9827 deletions

View File

@@ -1,8 +1,6 @@
name: Feature Request
description: Submit a new feature request.
title: "[Feature Request]: "
labels:
- enhancement
body:
- type: markdown
attributes:

View File

@@ -1,5 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: 💬 Discord Server
url: https://discord.gg/Cn8pWhQRxZ
url: https://discord.gg/HYmhKj45pU
about: You can join our Discord server for real time discussion and support

View File

@@ -9,8 +9,6 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
open-pull-requests-limit: 10
rebase-strategy: "auto"
- package-ecosystem: "docker"
directory: "/" # Location of Dockerfile
schedule:

View File

@@ -1,53 +1,20 @@
Translation:
translation:
- changed-files:
- any-glob-to-any-file: 'src/main/resources/messages_*_*.properties'
- any-glob-to-any-file: 'scripts/ignore_translation.toml'
Front End:
- changed-files:
- any-glob-to-any-file: 'src/main/resources/templates/**/*'
- any-glob-to-any-file: 'src/main/resources/static/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/**'
- any-glob-to-any-file: 'src/main/resources/templates/**'
Java:
java:
- changed-files:
- any-glob-to-any-file: 'src/main/java/**/*.java'
Back End:
- changed-files:
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/security/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/model/provider/**/*'
- any-glob-to-any-file: 'src/main/resources/settings.yml.template'
- any-glob-to-any-file: 'src/main/resources/banner.txt'
Security:
- changed-files:
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/security/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/model/provider/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/model/AuthenticationType.java'
API:
- changed-files:
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/MetricsController.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/**/*'
Documentation:
documentation:
- changed-files:
- any-glob-to-any-file: '**/*.md'
- any-glob-to-any-file: 'scripts/counter_translation.py'
- any-glob-to-any-file: 'scripts/ignore_translation.toml'
Docker:
docker:
- changed-files:
- any-glob-to-any-file: 'Dockerfile'
- any-glob-to-any-file: 'Dockerfile-*'
- any-glob-to-any-file: 'exampleYmlFiles/*.yml'
Test:
- changed-files:
- any-glob-to-any-file: 'cucumber/**/*'
- any-glob-to-any-file: 'src/test**/*'
Github:
- changed-files:
- any-glob-to-any-file: '.github/**/*'
- any-glob-to-any-file: 'Dockerfile-*'

93
.github/labels.yml vendored
View File

@@ -1,93 +0,0 @@
# Labels names are important as they are used by Release Drafter to decide
# regarding where to record them in changelog or if to skip them.
#
# The repository labels will be automatically configured using this file and
# the GitHub Action https://github.com/marketplace/actions/github-labeler.
- name: "Back End"
color: "20CE6C"
description: "Issues related to back-end development"
from_name: "Back end"
- name: "Bug"
description: "Something isn't working"
color: "EB9CA6"
from_name: "bug"
- name: "dependencies"
description: "Pull requests that update a dependency file"
color: "5AA8FC"
- name: "Docker"
description: "Pull requests that update Docker code"
color: "1FCEFF"
from_name: "docker"
- name: "Documentation"
description: "Improvements or additions to documentation"
color: "35ABFF"
from_name: "documentation"
- name: "Done for next release"
color: "0CDBD1"
- name: "Done"
color: "60F13B"
- name: "duplicate"
description: "This issue or pull request already exists"
color: "CDD1D5"
- name: "enhancement"
description: "New feature or request"
color: "A0EEEE"
- name: "fix needs confirmation"
color: "60A1E7"
description: "Fix needs to be confirmed"
- name: "Front End"
color: "BBD2F1"
description: "Issues related to front-end development"
- name: "github-actions"
description: "Pull requests that update GitHub Actions code"
color: "999999"
from_name: "github_actions"
- name: "good first issue"
description: "Good for newcomers"
color: "C1B8FF"
- name: "help wanted"
description: "Extra attention is needed"
color: "00E6C4"
- name: "invalid"
description: "This doesn't seem right"
color: "E5E566"
- name: "Java"
description: "Pull requests that update Java code"
color: "FF9E1F"
from_name: "java"
- name: "Long-term Enhancement"
color: "BFDEC3"
description: "Enhancements planned for the long term"
- name: "more-info-needed"
color: "00E4F8"
description: "More information is needed"
- name: "needs investigation"
color: "B8C3A7"
description: "Issues that require further investigation"
- name: "Prioritised enhancement"
color: "4BA2EE"
description: "High-priority enhancements"
- name: "question"
description: "Further information is requested"
color: "D97EE5"
- name: "Translation"
color: "9FABF9"
from_name: "translation"
- name: "upstream"
color: "DEDEDE"
- name: "v2"
color: "FFFF00"
- name: "wontfix"
description: "This will not be worked on"
color: "FFFFFF"
- name: "Security"
color: "000000"
description: "Security-related issues or pull requests"
- name: "API"
color: "FFFF00"
description: "API-related issues or pull requests"
- name: "Test"
color: "FF9E1F"
description: "Testing-related issues or pull requests"
- name: "Stale"
color: "000000"

View File

@@ -1,5 +1,4 @@
"""check_tabulator.py"""
import argparse
import sys

View File

@@ -1,4 +1,4 @@
name: Build repo
name: "Build repo"
on:
push:
@@ -17,72 +17,20 @@ jobs:
strategy:
fail-fast: false
matrix:
jdk-version: [17, 21]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up JDK ${{ matrix.jdk-version }}
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.jdk-version }}
java-version: "17"
distribution: "temurin"
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v3
with:
gradle-version: 8.7
- name: Build with Gradle
run: ./gradlew build --no-build-cache
docker-compose-tests:
# if: github.event_name == 'push' && github.ref == 'refs/heads/main' ||
# (github.event_name == 'pull_request' &&
# contains(github.event.pull_request.labels.*.name, 'licenses') == false &&
# (
# contains(github.event.pull_request.labels.*.name, 'Front End') ||
# contains(github.event.pull_request.labels.*.name, 'Java') ||
# contains(github.event.pull_request.labels.*.name, 'Back End') ||
# contains(github.event.pull_request.labels.*.name, 'Security') ||
# contains(github.event.pull_request.labels.*.name, 'API') ||
# contains(github.event.pull_request.labels.*.name, 'Docker') ||
# contains(github.event.pull_request.labels.*.name, 'Test')
# )
# )
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Java 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "adopt"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Install Docker Compose
run: |
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.7"
- name: Pip requirements
run: |
pip install -r ./cucumber/requirements.txt
- name: Run Docker Compose Tests
run: |
chmod +x ./test.sh
./test.sh

View File

@@ -11,9 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Apply Labels
uses: actions/labeler@v5
- uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler-config.yml

View File

@@ -45,7 +45,6 @@ jobs:
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
- name: Create Pull Request
id: cpr
if: env.CHANGES_DETECTED == 'true'
uses: peter-evans/create-pull-request@v6
with:
@@ -59,20 +58,5 @@ jobs:
body: |
Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
labels: licenses
draft: false
delete-branch: true
- name: Auto approve
if: steps.cpr.outputs.pull-request-operation == 'created'
run: gh pr review --approve "${{ steps.cpr.outputs.pull-request-number }}"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Enable auto-merge
if: steps.cpr.outputs.pull-request-operation == 'created'
uses: peter-evans/enable-pull-request-automerge@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
merge-method: squash # Choose the merge method: merge, squash, or rebase

View File

@@ -1,24 +0,0 @@
name: Manage labels
on:
schedule:
- cron: "30 20 * * *"
permissions:
contents: read
issues: write
jobs:
labeler:
name: Labeler
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v4
- name: Run Labeler
uses: crazy-max/ghaction-github-labeler@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
yaml-file: .github/labels.yml
skip-delete: true

View File

@@ -51,7 +51,6 @@ jobs:
[1]: https://github.com/peter-evans/create-pull-request
draft: false
delete-branch: true
labels: github-actions
sync-readme:
runs-on: ubuntu-latest
steps:
@@ -89,4 +88,3 @@ jobs:
[1]: https://github.com/peter-evans/create-pull-request
draft: false
delete-branch: true
labels: Documentation,Translation,github-actions

47
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: Docker Compose Tests
on:
pull_request:
paths:
- "src/**"
- "**.gradle"
- "!src/main/java/resources/messages*"
- "exampleYmlFiles/**"
- "Dockerfile"
- "Dockerfile**"
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Java 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "adopt"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Install Docker Compose
run: |
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.26.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# sudo chmod +x /usr/local/bin/docker-compose
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.7"
- name: Pip requirements
run: |
pip install -r ./cucumber/requirements.txt
- name: Run Docker Compose Tests
run: |
chmod +x ./test.sh
./test.sh

47
.gitignore vendored
View File

@@ -1,3 +1,5 @@
### Eclipse ###
.metadata
bin/
@@ -20,6 +22,7 @@ customFiles/
configs/
watchedFolders/
# Gradle
.gradle
.lock
@@ -116,48 +119,12 @@ watchedFolders/
*.db
/build
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*.pyo
# Virtual environments
.env*
.venv*
env*/
venv*/
ENV/
env.bak/
venv.bak/
# VS Code
/.vscode/**/*
!/.vscode/settings.json
# IntelliJ IDEA
.idea/
*.iml
out/
/.vscode
/.idea
# Ignore Mac DS_Store files
.DS_Store
**/.DS_Store
# cucumber
/cucumber/reports/**
# Certs
*.p12
*.pem
*.crt
*.cer
*.der
*.key
*.csr
# cache
.ruff_cache
.mypy_cache
.pytest_cache
.ipynb_checkpoints
#cucumber
/cucumber/reports/**

53
.vscode/settings.json vendored
View File

@@ -1,53 +0,0 @@
{
"java.compile.nullAnalysis.mode": "automatic",
"files.eol": "auto",
"java.configuration.updateBuildConfiguration": "interactive",
"black-formatter.args": ["--line-length", "127"],
"flake8.args": ["--max-line-length", "127"],
"pylint.args": ["max-line-length", "127"],
"[java]": {
"editor.tabSize": 4,
"editor.detectIndentation": false,
"editor.rulers": [127]
},
"[python]": {
"editor.tabSize": 2,
"editor.detectIndentation": false,
"editor.rulers": [127]
},
"[gradle-build]": {
"editor.tabSize": 4,
"editor.detectIndentation": false,
"editor.rulers": [127]
},
"[gradle]": {
"editor.tabSize": 4,
"editor.detectIndentation": false,
"editor.rulers": [127]
},
"[html]": {
"editor.tabSize": 2,
"editor.rulers": [127],
"files.trimFinalNewlines": false,
"files.insertFinalNewline": false
},
"[javascript]": {
"editor.tabSize": 2,
"editor.rulers": [127]
},
"[yaml]": {
"files.trimFinalNewlines": false,
"files.insertFinalNewline": false
},
"diffEditor.maxComputationTime": 0,
"editor.wordSegmenterLocales": null,
"editor.guides.bracketPairs": "active",
"editor.guides.bracketPairsHorizontal": "active",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true,
"editor.indentSize": "tabSize",
"editor.stickyScroll.enabled": false,
"editor.minimap.enabled": false,
"editor.formatOnSave": true
}

View File

@@ -1,5 +1,5 @@
# Main stage
FROM alpine:3.20.2
FROM alpine:3.20.0
# Copy necessary files
COPY scripts /scripts
@@ -45,8 +45,8 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
# CV
py3-opencv \
# python3/pip
python3 \
py3-pip && \
python3 && \
wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \
# uno unoconv and HTML
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
mv /usr/share/tessdata /usr/share/tessdata-original && \

View File

@@ -12,7 +12,7 @@ RUN DOCKER_ENABLE_SECURITY=true \
./gradlew clean build
# Main stage
FROM alpine:3.20.2
FROM alpine:3.20.0
# Copy necessary files
COPY scripts /scripts
@@ -31,7 +31,7 @@ ENV DOCKER_ENABLE_SECURITY=false \
PGID=1000 \
UMASK=022 \
FAT_DOCKER=true \
INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
INSTALL_BOOK_AND_ADVANCED_HTML_OPS=true
# JDK for app
@@ -45,6 +45,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
tini \
bash \
curl \
calibre@testing \
shadow \
su-exec \
openssl \
@@ -61,8 +62,8 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
# CV
py3-opencv \
# python3/pip
python3 \
py3-pip && \
python3 && \
wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \
# uno unoconv and HTML
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
mv /usr/share/tessdata /usr/share/tessdata-original && \

View File

@@ -1,5 +1,5 @@
# use alpine
FROM alpine:3.20.2
FROM alpine:3.20.0
ARG VERSION_TAG

View File

@@ -2,7 +2,7 @@
<h1 align="center">Stirling-PDF</h1>
[![Docker Pulls](https://img.shields.io/docker/pulls/frooodle/s-pdf)](https://hub.docker.com/r/frooodle/s-pdf)
[![Discord](https://img.shields.io/discord/1068636748814483718?label=Discord)](https://discord.gg/Cn8pWhQRxZ)
[![Discord](https://img.shields.io/discord/1068636748814483718?label=Discord)](https://discord.gg/HYmhKj45pU)
[![Docker Image Version (tag latest semver)](https://img.shields.io/docker/v/frooodle/s-pdf/latest)](https://github.com/Stirling-Tools/Stirling-PDF/)
[![GitHub Repo stars](https://img.shields.io/github/stars/stirling-tools/stirling-pdf?style=social)](https://github.com/Stirling-Tools/stirling-pdf)
[![Paypal Donate](https://img.shields.io/badge/Paypal%20Donate-yellow?style=flat&logo=paypal)](https://www.paypal.com/donate/?hosted_button_id=MN7JPG5G6G3JL)
@@ -165,46 +165,42 @@ Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR
## Supported Languages
Stirling PDF currently supports 38!
Stirling PDF currently supports 32!
| Language | Progress |
| ------------------------------------------- | -------------------------------------- |
| Arabic (العربية) (ar_AR) | ![45%](https://geps.dev/progress/45) |
| Basque (Euskara) (eu_ES) | ![61%](https://geps.dev/progress/61) |
| Bulgarian (Български) (bg_BG) | ![94%](https://geps.dev/progress/94) |
| Catalan (Català) (ca_CA) | ![48%](https://geps.dev/progress/48) |
| Croatian (Hrvatski) (hr_HR) | ![94%](https://geps.dev/progress/94) |
| Czech (Česky) (cs_CZ) | ![89%](https://geps.dev/progress/89) |
| Danish (Dansk) (da_DK) | ![9%](https://geps.dev/progress/9) |
| Dutch (Nederlands) (nl_NL) | ![95%](https://geps.dev/progress/95) |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![93%](https://geps.dev/progress/93) |
| German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) |
| Greek (Ελληνικά) (el_GR) | ![81%](https://geps.dev/progress/81) |
| Hindi (हिंदी) (hi_IN) | ![76%](https://geps.dev/progress/76) |
| Hungarian (Magyar) (hu_HU) | ![75%](https://geps.dev/progress/75) |
| Indonesia (Bahasa Indonesia) (id_ID) | ![76%](https://geps.dev/progress/76) |
| Irish (Gaeilge) (ga_IE) | ![98%](https://geps.dev/progress/98) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Japanese (日本語) (ja_JP) | ![92%](https://geps.dev/progress/92) |
| Korean (한국어) (ko_KR) | ![84%](https://geps.dev/progress/84) |
| Norwegian (Norsk) (no_NB) | ![97%](https://geps.dev/progress/97) |
| Polish (Polski) (pl_PL) | ![92%](https://geps.dev/progress/92) |
| Portuguese (Português) (pt_PT) | ![78%](https://geps.dev/progress/78) |
| Portuguese Brazilian (Português) (pt_BR) | ![85%](https://geps.dev/progress/85) |
| Romanian (Română) (ro_RO) | ![38%](https://geps.dev/progress/38) |
| Russian (Русский) (ru_RU) | ![83%](https://geps.dev/progress/83) |
| Sebian Latin alphabet (Srpski) (sr_LATN_RS) | ![78%](https://geps.dev/progress/78) |
| Arabic (العربية) (ar_AR) | ![45%](https://geps.dev/progress/45) |
| German (Deutsch) (de_DE) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![94%](https://geps.dev/progress/94) |
| Spanish (Español) (es_ES) | ![92%](https://geps.dev/progress/92) |
| Simplified Chinese (简体中文) (zh_CN) | ![98%](https://geps.dev/progress/98) |
| Slovakian (Slovensky) (sk_SK) | ![91%](https://geps.dev/progress/91) |
| Spanish (Español) (es_ES) | ![97%](https://geps.dev/progress/97) |
| Traditional Chinese (繁體中文) (zh_TW) | ![96%](https://geps.dev/progress/96) |
| Catalan (Català) (ca_CA) | ![48%](https://geps.dev/progress/48) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Swedish (Svenska) (sv_SE) | ![39%](https://geps.dev/progress/39) |
| Thai (ไทย) (th_TH) | ![99%](https://geps.dev/progress/99) |
| Traditional Chinese (繁體中文) (zh_TW) | ![97%](https://geps.dev/progress/97) |
| Turkish (Türkçe) (tr_TR) | ![98%](https://geps.dev/progress/98) |
| Ukrainian (Українська) (uk_UA) | ![89%](https://geps.dev/progress/89) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![98%](https://geps.dev/progress/98) |
| Polish (Polski) (pl_PL) | ![90%](https://geps.dev/progress/90) |
| Romanian (Română) (ro_RO) | ![39%](https://geps.dev/progress/39) |
| Korean (한국어) (ko_KR) | ![84%](https://geps.dev/progress/84) |
| Portuguese Brazilian (Português) (pt_BR) | ![60%](https://geps.dev/progress/60) |
| Portuguese (Português) (pt_PT) | ![78%](https://geps.dev/progress/78) |
| Russian (Русский) (ru_RU) | ![84%](https://geps.dev/progress/84) |
| Basque (Euskara) (eu_ES) | ![62%](https://geps.dev/progress/62) |
| Japanese (日本語) (ja_JP) | ![90%](https://geps.dev/progress/90) |
| Dutch (Nederlands) (nl_NL) | ![96%](https://geps.dev/progress/96) |
| Greek (Ελληνικά) (el_GR) | ![82%](https://geps.dev/progress/82) |
| Turkish (Türkçe) (tr_TR) | ![94%](https://geps.dev/progress/94) |
| Indonesia (Bahasa Indonesia) (id_ID) | ![76%](https://geps.dev/progress/76) |
| Hindi (हिंदी) (hi_IN) | ![77%](https://geps.dev/progress/77) |
| Hungarian (Magyar) (hu_HU) | ![76%](https://geps.dev/progress/76) |
| Bulgarian (Български) (bg_BG) | ![94%](https://geps.dev/progress/94) |
| Sebian Latin alphabet (Srpski) (sr_LATN_RS) | ![78%](https://geps.dev/progress/78) |
| Ukrainian (Українська) (uk_UA) | ![90%](https://geps.dev/progress/90) |
| Slovakian (Slovensky) (sk_SK) | ![92%](https://geps.dev/progress/92) |
| Czech (Česky) (cs_CZ) | ![90%](https://geps.dev/progress/90) |
| Croatian (Hrvatski) (hr_HR) | ![95%](https://geps.dev/progress/95) |
| Norwegian (Norsk) (no_NB) | ![96%](https://geps.dev/progress/96) |
## Contributing (creating issues, translations, fixing bugs, etc.)
@@ -216,7 +212,7 @@ Stirling PDF allows easy customization of the app.
Includes things like
- Custom application name
- Custom slogans, icons, HTML, images CSS etc (via file overrides)
- Custom slogans, icons, HTML, images CSS etc (via file overrides)
There are two options for this, either using the generated settings file ``settings.yml``
This file is located in the ``/configs`` directory and follows standard YAML formatting
@@ -266,7 +262,6 @@ security:
clientId: '' # Client ID from your provider
clientSecret: '' # Client Secret from your provider
autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
blockRegistration: false # set to 'true' to deny login with SSO without prior registration by an admin
useAsUsername: email # Default is 'email'; custom fields can be used as the username
scopes: openid, profile, email # Specify the scopes for which the application will request permissions
provider: google # Set this to your OAuth provider's name, e.g., 'google' or 'keycloak'

View File

@@ -1,32 +1,25 @@
plugins {
id "java"
id "org.springframework.boot" version "3.3.2"
id "io.spring.dependency-management" version "1.1.6"
id "org.springframework.boot" version "3.3.0"
id "io.spring.dependency-management" version "1.1.5"
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 "edu.sc.seis.launch4j" version "3.0.5"
id "com.diffplug.spotless" version "6.25.0"
id "com.github.jk1.dependency-license-report" version "2.9"
id "com.github.jk1.dependency-license-report" version "2.8"
}
import com.github.jk1.license.render.*
ext {
springBootVersion = "3.3.2"
pdfboxVersion = "3.0.3"
logbackVersion = "1.5.7"
imageioVersion = "3.11.0"
lombokVersion = "1.18.34"
bouncycastleVersion = "1.78.1"
springBootVersion = "3.3.0"
}
group = "stirling.software"
version = "0.28.0"
version = "0.26.1"
java {
// 17 is lowest but we support and recommend 21
sourceCompatibility = JavaVersion.VERSION_17
}
// 17 is lowest but we support and recommend 21
sourceCompatibility = "17"
repositories {
mavenCentral()
@@ -47,10 +40,8 @@ sourceSets {
exclude "stirling/software/SPDF/controller/web/AccountWebController.java"
exclude "stirling/software/SPDF/controller/web/DatabaseWebController.java"
exclude "stirling/software/SPDF/model/ApiKeyAuthenticationToken.java"
exclude "stirling/software/SPDF/model/AttemptCounter.java"
exclude "stirling/software/SPDF/model/Authority.java"
exclude "stirling/software/SPDF/model/PersistentLogin.java"
exclude "stirling/software/SPDF/model/SessionEntity.java"
exclude "stirling/software/SPDF/model/User.java"
exclude "stirling/software/SPDF/repository/**"
}
@@ -106,15 +97,15 @@ tasks.wrapper {
dependencies {
//security updates
implementation "ch.qos.logback:logback-classic:$logbackVersion"
implementation "ch.qos.logback:logback-core:$logbackVersion"
implementation "org.springframework:spring-webmvc:6.1.12"
implementation "ch.qos.logback:logback-classic:1.5.6"
implementation "ch.qos.logback:logback-core:1.5.6"
implementation "org.springframework:spring-webmvc:6.1.9"
implementation("io.github.pixee:java-security-toolkit:1.2.0")
implementation("io.github.pixee:java-security-toolkit:1.1.3")
// implementation "org.yaml:snakeyaml:2.2"
implementation 'com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4'
// Exclude Tomcat and include Jetty
implementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion") {
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
@@ -140,22 +131,22 @@ dependencies {
implementation "org.apache.xmlgraphics:batik-all:1.17"
// TwelveMonkeys
implementation "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
implementation "com.twelvemonkeys.imageio:imageio-bmp:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-hdr:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-icns:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-iff:$imageioVersion"
implementation "com.twelvemonkeys.imageio:imageio-jpeg:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-pcx:$imageioVersion@
// implementation "com.twelvemonkeys.imageio:imageio-pict:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-pnm:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-psd:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-sgi:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-tga:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-thumbsdb:$imageioVersion"
implementation "com.twelvemonkeys.imageio:imageio-tiff:$imageioVersion"
implementation "com.twelvemonkeys.imageio:imageio-webp:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-xwd:$imageioVersion"
implementation "com.twelvemonkeys.imageio:imageio-batik:3.10.1"
implementation "com.twelvemonkeys.imageio:imageio-bmp:3.10.1"
// implementation "com.twelvemonkeys.imageio:imageio-hdr:3.10.1"
// implementation "com.twelvemonkeys.imageio:imageio-icns:3.10.1"
// implementation "com.twelvemonkeys.imageio:imageio-iff:3.10.1"
implementation "com.twelvemonkeys.imageio:imageio-jpeg:3.11.0"
// implementation "com.twelvemonkeys.imageio:imageio-pcx:3.10.1"
// implementation "com.twelvemonkeys.imageio:imageio-pict:3.10.1"
// implementation "com.twelvemonkeys.imageio:imageio-pnm:3.10.1"
// implementation "com.twelvemonkeys.imageio:imageio-psd:3.10.1"
// implementation "com.twelvemonkeys.imageio:imageio-sgi:3.10.1"
// implementation "com.twelvemonkeys.imageio:imageio-tga:3.10.1"
// implementation "com.twelvemonkeys.imageio:imageio-thumbsdb:3.10.1"
implementation "com.twelvemonkeys.imageio:imageio-tiff:3.10.1"
implementation "com.twelvemonkeys.imageio:imageio-webp:3.10.1"
// implementation "com.twelvemonkeys.imageio:imageio-xwd:3.10.1"
implementation "commons-io:commons-io:2.16.1"
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0"
@@ -167,36 +158,33 @@ dependencies {
exclude group: "commons-logging", module: "commons-logging"
}
implementation ("org.apache.pdfbox:pdfbox:$pdfboxVersion") {
implementation ("org.apache.pdfbox:pdfbox:3.0.2") {
exclude group: "commons-logging", module: "commons-logging"
}
implementation ("org.apache.pdfbox:xmpbox:$pdfboxVersion") {
implementation ("org.apache.pdfbox:xmpbox:3.0.2") {
exclude group: "commons-logging", module: "commons-logging"
}
implementation 'org.apache.pdfbox:jbig2-imageio:3.0.4'
implementation "com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4"
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcprov-jdk18on:1.78.1"
implementation "org.bouncycastle:bcpkix-jdk18on:1.78.1"
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
implementation "io.micrometer:micrometer-core:1.13.3"
implementation "io.micrometer:micrometer-core:1.13.0"
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
// https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation "org.commonmark:commonmark:0.22.0"
implementation "org.commonmark:commonmark-ext-gfm-tables:0.22.0"
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
implementation "com.bucket4j:bucket4j_jdk17-core:8.13.1"
implementation "com.fathzer:javaluator:3.0.4"
implementation "com.bucket4j:bucket4j_jdk17-core:8.12.1"
implementation "com.fathzer:javaluator:3.0.4"
developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion")
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
compileOnly "org.projectlombok:lombok:1.18.32"
annotationProcessor "org.projectlombok:lombok:1.18.32"
testImplementation 'org.mockito:mockito-inline:5.2.0'
testImplementation 'org.mockito:mockito-inline:3.12.4'
}
tasks.withType(JavaCompile).configureEach {
@@ -207,7 +195,7 @@ compileJava {
options.compilerArgs << "-parameters"
}
task writeVersion {
task writeVersion {
def propsFile = file("src/main/resources/version.properties")
def props = new Properties()
props.setProperty("version", version)
@@ -237,6 +225,6 @@ tasks.named("test") {
useJUnitPlatform()
}
task printVersion {
task printVersion {
println project.version
}

View File

@@ -1,5 +1,5 @@
apiVersion: v2
appVersion: 0.28.0
appVersion: 0.26.1
description: locally hosted web application that allows you to perform various operations
on PDF files
home: https://github.com/Stirling-Tools/Stirling-PDF

View File

@@ -62,10 +62,8 @@ spec:
imagePullPolicy: {{ .Values.image.pullPolicy }}
securityContext:
{{- toYaml .Values.containerSecurityContext | nindent 10 }}
env:
- name: SYSTEM_ROOTURIPATH
value: {{ .Values.rootPath}}
{{- if .Values.envs }}
env:
{{ toYaml .Values.envs | indent 8 }}
{{- end }}
{{- if .Values.extraArgs }}
@@ -77,13 +75,13 @@ spec:
containerPort: 8080
livenessProbe:
httpGet:
path: {{ .Values.rootPath}}
path: /
port: http
{{ toYaml .Values.probes.livenessHttpGetConfig | indent 12 }}
{{ toYaml .Values.probes.liveness | indent 10 }}
readinessProbe:
httpGet:
path: {{ .Values.rootPath}}
path: /
port: http
{{ toYaml .Values.probes.readinessHttpGetConfig | indent 12 }}
{{ toYaml .Values.probes.readiness | indent 10 }}

View File

@@ -15,9 +15,6 @@ secret:
commonLabels: {}
# team_name: dev
# rootpath for the application
rootPath: /
envs: []
# - name: UI_APP_NAME
# value: "Stirling PDF"
@@ -27,6 +24,8 @@ envs: []
# value: "Stirling PDF"
# - name: ALLOW_GOOGLE_VISIBILITY
# value: "true"
# - name: APP_ROOT_PATH
# value: "/"
# - name: APP_LOCALE
# value: "en_GB"

View File

@@ -1,106 +0,0 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 9 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/Contents 10 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/Contents 11 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
>>
endobj
7 0 obj
<<
/Author (anonymous) /CreationDate (D:20240718233034+00'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20240718233034+00'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (unspecified) /Title (untitled) /Trapped /False
>>
endobj
8 0 obj
<<
/Count 3 /Kids [ 3 0 R 4 0 R 5 0 R ] /Type /Pages
>>
endobj
9 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 210
>>
stream
Gap@Gb79+X'F"5[`EfJOD4:mD<%*=m+N>oDG,>NK`<U'B^0WYY,dWl^i_UcRk`<"L=<NPC$BtQ<5l$3<Y!?BuoCSYQ6GSt25lpqr0IrP?S[b)9%M"e'HHFqcRO'9eRaR0'DYi*Y.:nEMFAoTM;rPL%EF]`CfoELVl_Q,"LS:%iI;Nc[&bG.*65O]ecfK1'*<>5P_s[usI/ph*0pV~>endstream
endobj
10 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 209
>>
stream
Gap@Gb79+X'F"5Y`EfJOV2A9=!fB]F'tK1LS`,]G+MiTenb&V2-^hqa(5IE#Nr59/!"Qm*5_(BdF!0&h!Yhk/A+\iS'%6tuO$O)9LaZS+flr([1p2&#RS1p/gT[B;rDj-=&=iqUlj(P^/5U@eCFqn4:<lU`l`.HXqG-',hJH.DI.(6L\luSAW`Q'oje[qgVLVIXg%PXe+,<$7('~>endstream
endobj
11 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 209
>>
stream
Gap@GbmK%f(e+0_`ODoa2.):e/i+N3r(.o*Qf\gSNb(bt4FIubi@GIOE=p8Ir3;CbQ@KuG^cdJhODZKQ*upt+*rdZ%!mFmN$*.P)K;`s#]G=8AO3s3DGB.RCOn?[F]bEIg,a>25?B%dh\Z/C6opFE'el@I,P\u\V\]:*JYrrsNJ&d,11VL;$h!43eGu&1X6$+5-h\Vr6!+>4Je,~>endstream
endobj
xref
0 12
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000404 00000 n
0000000598 00000 n
0000000792 00000 n
0000000860 00000 n
0000001156 00000 n
0000001227 00000 n
0000001527 00000 n
0000001827 00000 n
trailer
<<
/ID
[<0d5cf047e754e05f8d574f067785875c><0d5cf047e754e05f8d574f067785875c>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 7 0 R
/Root 6 0 R
/Size 12
>>
startxref
2127
%%EOF

View File

@@ -1,106 +0,0 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 9 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/Contents 10 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/Contents 11 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
>>
endobj
7 0 obj
<<
/Author (anonymous) /CreationDate (D:20240718233034+00'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20240718233034+00'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (unspecified) /Title (untitled) /Trapped /False
>>
endobj
8 0 obj
<<
/Count 3 /Kids [ 3 0 R 4 0 R 5 0 R ] /Type /Pages
>>
endobj
9 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 207
>>
stream
Gap@G:CDb.*/<p2MVk["e@)7*Z0@"b%+@f/9pA%_U<oOkVp?PnGRb81iPg?0i?(]%^_CSf##%;<!7Ne/-%RR^p@t7hKYZ9eJVHV]fjjHIB:6DrW+2\p16@*`r^CpQZZH'2Pjqd<.&hM2UO%$Wi$te%4QmS;<E"QS\!deQG_XtuEK>b(UbS>%`/0S`k\\5'TNY0mmgH?`8]i_0~>endstream
endobj
10 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 207
>>
stream
Gap@G]afWJ'Lm;=if<;s>V*7BTJ]oQ@P!(q5S+WG1%>L@?8Ue;c>[fY&&IOd5@t@TY@+q.5T<Z'81"J("KhsBa+&u4"n'#6)AjfImh)%$0tVC:aGk",=aJJH#/4]i.WJr9c"cibYm:M-44<%FFlG0Cl\Z'nmo7C"TR+7dk3T#iD(9Pq'\;rQku%o>A_`50SO&7M04=8M'O<Am~>endstream
endobj
11 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 209
>>
stream
Gap@GYmu@>'Ld5[if35r/JNaJ.A.7fP9RpSN*8k^-sEER0,enq1Rsuo@R/uCO-^&Y`F'9d^a?9)?ns+F&dXm[HMgPn6Ep+%TRk5Nh+!(+[H#H:U^.^(YL,PKS'%j/:3O\hJVEK-UUekJTd[A$N^((K^#0Du`i@,/^f5KiUISGr")3/+f9NF8NO1+iUgm^b"X\cE^+[:s!0]Gu6i~>endstream
endobj
xref
0 12
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000404 00000 n
0000000598 00000 n
0000000792 00000 n
0000000860 00000 n
0000001156 00000 n
0000001227 00000 n
0000001524 00000 n
0000001822 00000 n
trailer
<<
/ID
[<407fc55425168745e56176202aad30c9><407fc55425168745e56176202aad30c9>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 7 0 R
/Root 6 0 R
/Size 12
>>
startxref
2122
%%EOF

View File

@@ -1,106 +0,0 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 9 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/Contents 10 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/Contents 11 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
>>
endobj
7 0 obj
<<
/Author (anonymous) /CreationDate (D:20240718233034+00'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20240718233034+00'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (unspecified) /Title (untitled) /Trapped /False
>>
endobj
8 0 obj
<<
/Count 3 /Kids [ 3 0 R 4 0 R 5 0 R ] /Type /Pages
>>
endobj
9 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 209
>>
stream
Gap@G]+0EH(e/_@iZH]:>:>hu1e>07BJg5<'#:.C1n)e#(QJ6R1Rsuo_gpn.+0-H5$/#"iYR[B.9\'>7!aDAC*rf/t&6O#aH<?-7IT'\?X(&TcABG=ON*Nq`4k=o&p@3,0*31r<)TAP2Pk94p0\"R-_sY1$AYo[8B\?4R>feLAB\mpjZhp"`@J3;"Fm97#9+W,"eb95\+#p\^HN~>endstream
endobj
10 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 209
>>
stream
Gap@G]+0EX'Eriuig+>QHNeD'#n%Sq#n%BW`C'uDUOYK)HdS4E9JMsp+HUmDj&H-t*4?UamXX0peVspk"i_@ba+&u"J>UYDKV_^G,7V==aTZZ<YO7:sNSQ[6"Ja-29NtYjd#=`J@D'h+[QW=:EEb?A<k!f+\`g^?,Vgp7_)91[lR\f.Tkf7VIPLVYM&deF!aYt9Ip^"N",3F'*W~>endstream
endobj
11 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 209
>>
stream
Gap@G]+0EH(e/_@iZH]:>J`g!jPCLm;?AgU"fdk"PQZD\d?lRI_oWc[$tp^]O\:3fK8kWeX2&Jcg0+RoJ]j;2j*upu!b4.o&f)b$I@7CfIYjP^#\VjhC=QhQ]^lV-@<0Tam!0.+Dn@("AK%N,Uc7hb+6VoQ$q2q[7]BB92RoY/.j2N028i1jNf'@<1+Fqf$1&"8omHk`#DHP>OT~>endstream
endobj
xref
0 12
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000404 00000 n
0000000598 00000 n
0000000792 00000 n
0000000860 00000 n
0000001156 00000 n
0000001227 00000 n
0000001526 00000 n
0000001826 00000 n
trailer
<<
/ID
[<80da26147a484f2b7573da8151a93d2e><80da26147a484f2b7573da8151a93d2e>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 7 0 R
/Root 6 0 R
/Size 12
>>
startxref
2126
%%EOF

File diff suppressed because it is too large Load Diff

View File

@@ -1,106 +0,0 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 9 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/Contents 10 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/Contents 11 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
>>
endobj
7 0 obj
<<
/Author (anonymous) /CreationDate (D:20240718233034+00'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20240718233034+00'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (unspecified) /Title (untitled) /Trapped /False
>>
endobj
8 0 obj
<<
/Count 3 /Kids [ 3 0 R 4 0 R 5 0 R ] /Type /Pages
>>
endobj
9 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 206
>>
stream
Gap@G\IO3f&4Lr[@S4&T2aReWZ3N'9",Ncra>5AuK^J(o@r?=EP>b]h[L@XZ8q7#[c:#H2:^/=b,p3^,&f-Q.'H%!U?%N\iVa1pLMlh/41\A8@dF5@0al:-1?L;D%LpL3g\9`.3c6N/Mp=sE/nO%^@%Cc3`]e`qqS@[pkUWemMZC<P\fkqa55u)*hIUoU437-gb!e_*&B/,&~>endstream
endobj
10 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 209
>>
stream
Gap@G\IO3V'LdA_ig"8P1PS=kA5Q_GQ\P]*S3\>Q`jHYt?8UdkV`6]UV*On)+1VMV+A@.iF:*6sWfM9f"s.NmVuMto!p7-+,Rb<.h,pdi-&OQ5KO\RRFj.j"A)ScTQ7$hudF^TnZ'XuQA5"O]rYkt><-DJmj'"Ri>n!4`^m409XX`e)AR'*rGsn6m79.18+^ba=qRuss"-A3k+9~>endstream
endobj
11 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 210
>>
stream
Gap@G]+0EH(e/_@iZH]:.1fBHK`Xl'[i1&AjX(\k8hbgo(QJ6R1Rsuo6_I1A5Gg$JL;D#$J2CX;+Cf*cUHk2%H1XmpWe+qZ5moJ#B]>b%%[d,mfSSkS4A:Q4NlOFfrL7eA,s45"eUSakM;927AA,1"-LZ)&nZ/ah=8_X7:?ZMj@J@;r7d`t]Z0\d39M%:$k8[S5D"2oSap4s80l?~>endstream
endobj
xref
0 12
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000404 00000 n
0000000598 00000 n
0000000792 00000 n
0000000860 00000 n
0000001156 00000 n
0000001227 00000 n
0000001523 00000 n
0000001823 00000 n
trailer
<<
/ID
[<88edee24ee67bd7d6b7cf53cfa2222b0><88edee24ee67bd7d6b7cf53cfa2222b0>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 7 0 R
/Root 6 0 R
/Size 12
>>
startxref
2124
%%EOF

View File

@@ -1,106 +0,0 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 9 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/Contents 10 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/Contents 11 0 R /MediaBox [ 0 0 612 792 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
>>
endobj
7 0 obj
<<
/Author (anonymous) /CreationDate (D:20240718233034+00'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20240718233034+00'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (unspecified) /Title (untitled) /Trapped /False
>>
endobj
8 0 obj
<<
/Count 3 /Kids [ 3 0 R 4 0 R 5 0 R ] /Type /Pages
>>
endobj
9 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 209
>>
stream
Gap@GYmu@>'Ld5[if35rI0]sG)F[U^"c>T)"\\os-r:1V0,enq1Rsuo,*67.@k7U.LRF-P.e"CM2V!>iYi<g`nXh!K?n@$t^rY1$+^0'>=B8H6e;F1WmG#,(eS00(Qe9&:O@nI879DTsT,njXAB?`8:>,Hn3*RV!qh4;&@6%]<9Y*>QZ].Z5o;RAZXg7d[#+bphHs_Ep!QR2TZ2~>endstream
endobj
10 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 210
>>
stream
Gap@G]+0EH(e/_@iZH]:>=,iY1bE)XN?M;1'J/>i&HY;gks]*rj:!DKpb8@`prC#N+9E#o#-<G*!#p7e6j-1sX2k5S,6XmM"taYkfK^k">%usEeEk=sR<UT"dm`rXD;!S`_jS9LU+(R%e'V%WSMfHP.pXZEQqTQq=&D[I[PS(41(NIAZ1R/U?:Z=hSXu!NDF)bpG2F+/I/q/u1-Y~>endstream
endobj
11 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 209
>>
stream
Gap@G_$YcZ'LhbF`EQB$nqi=8S<;#HbK3&f>rnodRPo`Vf4P[3cJidY(I=[K5NWCT'<lHgci?oCRVNST&[k#q4oSC0FWgAt1pD4d_(hIRjn_Nt+cFgJlfm[1U8@/M4r^Pk<@F!@e?%/!-Vq;]nfdLi9]P2M)ck9?)%oNXa_\N<-d"(pjlH%-G`T@Sj&P(j6.@#Xh\Vr6!1iI2/H~>endstream
endobj
xref
0 12
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000404 00000 n
0000000598 00000 n
0000000792 00000 n
0000000860 00000 n
0000001156 00000 n
0000001227 00000 n
0000001526 00000 n
0000001827 00000 n
trailer
<<
/ID
[<4fcc82a085fe71e34a32d1b23c8b939f><4fcc82a085fe71e34a32d1b23c8b939f>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 7 0 R
/Root 6 0 R
/Size 12
>>
startxref
2127
%%EOF

View File

@@ -14,8 +14,3 @@ def after_scenario(context, scenario):
os.remove('response_file')
if hasattr(context, 'file_name') and os.path.exists(context.file_name):
os.remove(context.file_name)
# 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)

View File

@@ -1,4 +1,4 @@
@example @general
@example
Feature: API Validation
@positive @password
@@ -92,10 +92,10 @@ Feature: API Validation
| threshold | 90 |
| whitePercent | 99.9 |
When I send the API request to the endpoint "/api/v1/misc/remove-blanks"
Then the response content type should be "application/octet-stream"
And the response file should have extension ".zip"
And the response ZIP should contain 1 files
Then the response content type should be "application/pdf"
And the response file should have size greater than 0
And the response PDF should contain 0 pages
And the response status code should be 200
@positive @flatten
Scenario: Flatten PDF
@@ -127,4 +127,4 @@ Feature: API Validation
And the response PDF metadata should include "Title" as "Sample Title"
And the response status code should be 200

View File

@@ -32,7 +32,7 @@ Feature: API Validation
@ocr @positive
Scenario: Extract Image Scans
Given I generate a PDF file as "fileInput"
And the pdf contains 3 images of size 300x300 on 2 pages
And the pdf contains 3 images on 2 pages
And the request data includes
| parameter | value |
| angleThreshold | 5 |
@@ -125,7 +125,8 @@ Feature: API Validation
@ocr
Scenario: PDFA
Given I use an example file at "exampleFiles/pdfa2.pdf" as parameter "fileInput"
Given I generate a PDF file as "fileInput"
And the pdf contains 3 pages with random text
And the request data includes
| parameter | value |
| outputFormat | pdfa |
@@ -136,7 +137,8 @@ Feature: API Validation
@ocr
Scenario: PDFA1
Given I use an example file at "exampleFiles/pdfa1.pdf" as parameter "fileInput"
Given I generate a PDF file as "fileInput"
And the pdf contains 3 pages with random text
And the request data includes
| parameter | value |
| outputFormat | pdfa-1 |
@@ -147,7 +149,8 @@ Feature: API Validation
@compress @ghostscript @positive
Scenario: Compress
Given I use an example file at "exampleFiles/ghost3.pdf" as parameter "fileInput"
Given I generate a PDF file as "fileInput"
And the pdf contains 3 pages with random text
And the request data includes
| parameter | value |
| optimizeLevel | 4 |
@@ -158,7 +161,8 @@ Feature: API Validation
@compress @ghostscript @positive
Scenario: Compress
Given I use an example file at "exampleFiles/ghost2.pdf" as parameter "fileInput"
Given I generate a PDF file as "fileInput"
And the pdf contains 3 pages with random text
And the request data includes
| parameter | value |
| optimizeLevel | 1 |
@@ -171,7 +175,8 @@ Feature: API Validation
@compress @ghostscript @positive
Scenario: Compress
Given I use an example file at "exampleFiles/ghost1.pdf" as parameter "fileInput"
Given I generate a PDF file as "fileInput"
And the pdf contains 3 pages with random text
And the request data includes
| parameter | value |
| optimizeLevel | 1 |

View File

@@ -94,23 +94,3 @@ Feature: API Validation
| 1 | 10 | 2 | 10 |
@extract-images
Scenario Outline: Extract Image Scans
Given I use an example file at "exampleFiles/images.pdf" as parameter "fileInput"
And the request data includes
| parameter | value |
| format | <format> |
When I send the API request to the endpoint "/api/v1/misc/extract-images"
Then the response content type should be "application/octet-stream"
And the response file should have extension ".zip"
And the response ZIP should contain 20 files
And the response file should have size greater than 0
And the response status code should be 200
Examples:
| format |
| png |
| gif |
| jpeg |

View File

@@ -6,14 +6,11 @@ import io
import random
import string
from reportlab.lib.pagesizes import letter
from reportlab.lib.utils import ImageReader
from reportlab.pdfgen import canvas
import mimetypes
import requests
import zipfile
import shutil
import re
from PIL import Image, ImageDraw
#########
# GIVEN #
@@ -46,6 +43,8 @@ def step_use_example_file(context, filePath, fileInput):
except FileNotFoundError:
raise FileNotFoundError(f"The example file '{filePath}' does not exist.")
@given('the pdf contains {page_count:d} pages')
def step_pdf_contains_pages(context, page_count):
writer = PdfWriter()
@@ -67,6 +66,8 @@ def step_pdf_contains_blank_pages(context, page_count):
context.files[context.param_name].close()
context.files[context.param_name] = open(context.file_name, 'rb')
def create_black_box_image(file_name, size):
can = canvas.Canvas(file_name, pagesize=size)
width, height = size
@@ -75,75 +76,36 @@ def create_black_box_image(file_name, size):
can.showPage()
can.save()
@given(u'the pdf contains {image_count:d} images of size {width:d}x{height:d} on {page_count:d} pages')
def step_impl(context, image_count, width, height, page_count):
context.param_name = "fileInput"
context.file_name = "genericNonCustomisableName.pdf"
create_pdf_with_images_and_boxes(context.file_name, image_count, page_count, width, height)
if not hasattr(context, 'files'):
context.files = {}
context.files[context.param_name] = open(context.file_name, 'rb')
def add_black_boxes_to_image(image):
if isinstance(image, str):
image = Image.open(image)
draw = ImageDraw.Draw(image)
draw.rectangle([(0, 0), image.size], fill=(0, 0, 0)) # Fill image with black
return image
def create_pdf_with_images_and_boxes(file_name, image_count, page_count, image_width, image_height):
page_width, page_height = max(letter[0], image_width), max(letter[1], image_height)
def create_pdf_with_black_boxes(file_name, image_count, page_count):
page_width, page_height = letter
box_size = 72 # 1 inch by 1 inch black box
boxes_per_page = image_count // page_count + (1 if image_count % page_count != 0 else 0)
writer = PdfWriter()
box_counter = 0
for page in range(page_count):
packet = io.BytesIO()
can = canvas.Canvas(packet, pagesize=(page_width, page_height))
can = canvas.Canvas(packet, pagesize=letter)
for i in range(boxes_per_page):
if box_counter >= image_count:
break
# Simulating a dynamic image creation (replace this with your actual image creation logic)
# For demonstration, we'll create a simple black image
dummy_image = Image.new('RGB', (image_width, image_height), color='white') # Create a white image
dummy_image = add_black_boxes_to_image(dummy_image) # Add black boxes
# Convert the PIL Image to bytes to pass to drawImage
image_bytes = io.BytesIO()
dummy_image.save(image_bytes, format='PNG')
image_bytes.seek(0)
# Check if the image fits in the current page dimensions
x = (i % (page_width // image_width)) * image_width
y = page_height - (((i % (page_height // image_height)) + 1) * image_height)
if x + image_width > page_width or y < 0:
break
# Add the image to the PDF
can.drawImage(ImageReader(image_bytes), x, y, width=image_width, height=image_height)
x = (i % (page_width // box_size)) * box_size
y = page_height - ((i // (page_width // box_size) + 1) * box_size)
can.setFillColorRGB(0, 0, 0)
can.rect(x, y, box_size, box_size, fill=1)
box_counter += 1
can.showPage()
can.save()
packet.seek(0)
new_pdf = PdfReader(packet)
writer.add_page(new_pdf.pages[0])
# Write the PDF to file
with open(file_name, 'wb') as f:
writer.write(f)
# Clean up temporary image files
for i in range(image_count):
temp_image_path = f"temp_image_{i}.png"
if os.path.exists(temp_image_path):
os.remove(temp_image_path)
@given('the pdf contains {image_count:d} images on {page_count:d} pages')
def step_pdf_contains_images(context, image_count, page_count):
if not hasattr(context, 'param_name'):
@@ -156,6 +118,7 @@ def step_pdf_contains_images(context, image_count, page_count):
context.files[context.param_name].close()
context.files[context.param_name] = open(context.file_name, 'rb')
@given('the pdf contains {page_count:d} pages with random text')
def step_pdf_contains_pages_with_random_text(context, page_count):
buffer = io.BytesIO()
@@ -223,21 +186,6 @@ def save_generated_pdf(context, filename):
# WHEN #
########
@when('I send a GET request to "{endpoint}"')
def step_send_get_request(context, endpoint):
base_url = "http://localhost:8080"
full_url = f"{base_url}{endpoint}"
response = requests.get(full_url)
context.response = response
@when('I send a GET request to "{endpoint}" with parameters')
def step_send_get_request_with_params(context, endpoint):
base_url = "http://localhost:8080"
params = {row['parameter']: row['value'] for row in context.table}
full_url = f"{base_url}{endpoint}"
response = requests.get(full_url, params=params)
context.response = response
@when('I send the API request to the endpoint "{endpoint}"')
def step_send_api_request(context, endpoint):
url = f"http://localhost:8080{endpoint}"
@@ -330,6 +278,7 @@ def step_save_response_file(context, filename):
f.write(context.response.content)
print(f"Saved response content to {filename}")
@then('the response PDF should contain {page_count:d} pages')
def step_check_response_pdf_page_count(context, page_count):
response_file = io.BytesIO(context.response.content)
@@ -356,26 +305,3 @@ def step_check_response_zip_doc_page_count(context, doc_count, pages_per_doc):
reader = PdfReader(pdf_file)
actual_pages_per_doc = len(reader.pages)
assert actual_pages_per_doc == pages_per_doc, f"Expected {pages_per_doc} pages per document but got {actual_pages_per_doc} pages in document {file_name}"
@then('the JSON value of "{key}" should be "{expected_value}"')
def step_check_json_value(context, key, expected_value):
actual_value = context.response.json().get(key)
assert actual_value == expected_value, \
f"Expected JSON value for '{key}' to be '{expected_value}' but got '{actual_value}'"
@then('JSON list entry containing "{identifier_key}" as "{identifier_value}" should have "{target_key}" as "{target_value}"')
def step_check_json_list_entry(context, identifier_key, identifier_self, target_key, target_value):
json_response = context.response.json()
for entry in json_response:
if entry.get(identifier_key) == identifier_value:
assert entry.get(target_key) == target_value, \
f"Expected {target_key} to be {target_value} in entry where {identifier_key} is {identifier_value}, but found {entry.get(target_key)}"
break
else:
raise AssertionError(f"No entry with {identifier_key} as {identifier_value} found")
@then('the response should match the regex "{pattern}"')
def step_response_matches_regex(context, pattern):
response_text = context.response.text
assert re.match(pattern, response_text), \
f"Response '{response_text}' does not match the expected pattern '{pattern}'"

View File

@@ -22,6 +22,7 @@ services:
DOCKER_ENABLE_SECURITY: "false"
SECURITY_ENABLELOGIN: "false"
LANGS: "en_GB,en_US,ar_AR,de_DE,fr_FR,es_ES,zh_CN,zh_TW,ca_CA,it_IT,sv_SE,pl_PL,ro_RO,ko_KR,pt_BR,ru_RU,el_GR,hi_IN,hu_HU,tr_TR,id_ID"
INSTALL_BOOK_AND_ADVANCED_HTML_OPS: "true"
SYSTEM_DEFAULTLOCALE: en-US
UI_APPNAME: Stirling-PDF
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest

View File

@@ -25,11 +25,6 @@ ignore = [
'text',
]
[da_DK]
ignore = [
'language.direction',
]
[de_DE]
ignore = [
'AddStampRequest.alphabet',
@@ -92,11 +87,6 @@ ignore = [
'watermark.type.2',
]
[ga_IE]
ignore = [
'language.direction',
]
[hi_IN]
ignore = [
'language.direction',
@@ -222,14 +212,6 @@ ignore = [
'language.direction',
]
[th_TH]
ignore = [
'language.direction',
'pipeline.title',
'pipelineOptions.pipelineHeader',
'showJS.tags',
]
[tr_TR]
ignore = [
'language.direction',
@@ -240,14 +222,6 @@ ignore = [
'language.direction',
]
[vi_VN]
ignore = [
'language.direction',
'pipeline.title',
'pipelineOptions.pipelineHeader',
'showJS.tags',
]
[zh_CN]
ignore = [
'language.direction',

View File

@@ -12,8 +12,7 @@ fi
umask "$UMASK" || true
if [[ "$INSTALL_BOOK_AND_ADVANCED_HTML_OPS" == "true" && "$FAT_DOCKER" != "true" ]]; then
echo "issue with calibre in current version, feature currently disabled on Stirling-PDF"
#apk add --no-cache calibre@testing
apk add --no-cache calibre@testing
fi
if [[ "$FAT_DOCKER" != "true" ]]; then

View File

@@ -45,6 +45,7 @@ public class SPdfApplication {
// Check if the BROWSER_OPEN environment variable is set to true
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
if (browserOpen) {
try {
String url = "http://localhost:" + getNonStaticPort();
@@ -78,14 +79,13 @@ public class SPdfApplication {
// custom javs settings file
if (Files.exists(Paths.get("configs/custom_settings.yml"))) {
String existingLocation =
propertyFiles.getOrDefault("spring.config.additional-location", "");
if (!existingLocation.isEmpty()) {
existingLocation += ",";
String existing = propertyFiles.getOrDefault("spring.config.additional-location", "");
if (!existing.isEmpty()) {
existing += ",";
}
propertyFiles.put(
"spring.config.additional-location",
existingLocation + "file:configs/custom_settings.yml");
existing + "file:configs/custom_settings.yml");
} else {
logger.warn("Custom configuration file 'configs/custom_settings.yml' does not exist.");
}

View File

@@ -32,25 +32,25 @@ public class CleanUrlInterceptor implements HandlerInterceptor {
String queryString = request.getQueryString();
if (queryString != null && !queryString.isEmpty()) {
String requestURI = request.getRequestURI();
Map<String, String> allowedParameters = new HashMap<>();
Map<String, String> parameters = new HashMap<>();
// Keep only the allowed parameters
String[] queryParameters = queryString.split("&");
for (String param : queryParameters) {
String[] keyValuePair = param.split("=");
if (keyValuePair.length != 2) {
String[] keyValue = param.split("=");
if (keyValue.length != 2) {
continue;
}
if (ALLOWED_PARAMS.contains(keyValuePair[0])) {
allowedParameters.put(keyValuePair[0], keyValuePair[1]);
if (ALLOWED_PARAMS.contains(keyValue[0])) {
parameters.put(keyValue[0], keyValue[1]);
}
}
// If there are any parameters that are not allowed
if (allowedParameters.size() != queryParameters.length) {
if (parameters.size() != queryParameters.length) {
// Construct new query string
StringBuilder newQueryString = new StringBuilder();
for (Map.Entry<String, String> entry : allowedParameters.entrySet()) {
for (Map.Entry<String, String> entry : parameters.entrySet()) {
if (newQueryString.length() > 0) {
newQueryString.append("&");
}

View File

@@ -137,7 +137,6 @@ public class EndpointConfiguration {
addEndpointToGroup("Other", "auto-rename");
addEndpointToGroup("Other", "get-info-on-pdf");
addEndpointToGroup("Other", "show-javascript");
addEndpointToGroup("Other", "remove-image-pdf");
// CLI
addEndpointToGroup("CLI", "compress-pdf");
@@ -222,7 +221,6 @@ public class EndpointConfiguration {
addEndpointToGroup("Java", "split-pdf-by-sections");
addEndpointToGroup("Java", REMOVE_BLANKS);
addEndpointToGroup("Java", "pdf-to-text");
addEndpointToGroup("Java", "remove-image-pdf");
// Javascript
addEndpointToGroup("Javascript", "pdf-organizer");

View File

@@ -3,8 +3,9 @@ package stirling.software.SPDF.config.security;
import java.io.IOException;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
@@ -14,16 +15,17 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.User;
@Slf4j
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private LoginAttemptService loginAttemptService;
private UserService userService;
private static final Logger logger =
LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);
public CustomAuthenticationFailureHandler(
final LoginAttemptService loginAttemptService, UserService userService) {
this.loginAttemptService = loginAttemptService;
@@ -37,17 +39,14 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
AuthenticationException exception)
throws IOException, ServletException {
if (exception instanceof DisabledException) {
log.error("User is deactivated: ", exception);
getRedirectStrategy().sendRedirect(request, response, "/logout?userIsDisabled=true");
return;
}
String ip = request.getRemoteAddr();
log.error("Failed login attempt from IP: {}", ip);
logger.error("Failed login attempt from IP: {}", ip);
if (exception instanceof LockedException) {
getRedirectStrategy().sendRedirect(request, response, "/login?error=locked");
String contextPath = request.getContextPath();
if (exception.getClass().isAssignableFrom(InternalAuthenticationServiceException.class)
|| "Password must not be null".equalsIgnoreCase(exception.getMessage())) {
response.sendRedirect(contextPath + "/login?error=oauth2AuthenticationError");
return;
}
@@ -55,25 +54,20 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
Optional<User> optUser = userService.findByUsernameIgnoreCase(username);
if (username != null && optUser.isPresent() && !isDemoUser(optUser)) {
log.info(
logger.info(
"Remaining attempts for user {}: {}",
username,
optUser.get().getUsername(),
loginAttemptService.getRemainingAttempts(username));
loginAttemptService.loginFailed(username);
if (loginAttemptService.isBlocked(username) || exception instanceof LockedException) {
getRedirectStrategy().sendRedirect(request, response, "/login?error=locked");
if (loginAttemptService.isBlocked(username)
|| exception.getClass().isAssignableFrom(LockedException.class)) {
response.sendRedirect(contextPath + "/login?error=locked");
return;
}
}
if (exception instanceof BadCredentialsException
|| exception instanceof UsernameNotFoundException) {
getRedirectStrategy().sendRedirect(request, response, "/login?error=badcredentials");
return;
}
if (exception instanceof InternalAuthenticationServiceException
|| "Password must not be null".equalsIgnoreCase(exception.getMessage())) {
getRedirectStrategy()
.sendRedirect(request, response, "/login?error=oauth2AuthenticationError");
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)
|| exception.getClass().isAssignableFrom(UsernameNotFoundException.class)) {
response.sendRedirect(contextPath + "/login?error=badcredentials");
return;
}

View File

@@ -10,20 +10,15 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.utils.RequestUriUtils;
@Slf4j
public class CustomAuthenticationSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler {
private LoginAttemptService loginAttemptService;
private UserService userService;
public CustomAuthenticationSuccessHandler(
LoginAttemptService loginAttemptService, UserService userService) {
public CustomAuthenticationSuccessHandler(LoginAttemptService loginAttemptService) {
this.loginAttemptService = loginAttemptService;
this.userService = userService;
}
@Override
@@ -32,10 +27,6 @@ public class CustomAuthenticationSuccessHandler
throws ServletException, IOException {
String userName = request.getParameter("username");
if (userService.isUserDisabled(userName)) {
getRedirectStrategy().sendRedirect(request, response, "/logout?userIsDisabled=true");
return;
}
loginAttemptService.loginSucceeded(userName);
// Get the saved request

View File

@@ -2,26 +2,32 @@ package stirling.software.SPDF.config.security;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
@Autowired SessionRegistry sessionRegistry;
@Override
public void onLogoutSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
if (request.getParameter("userIsDisabled") != null) {
getRedirectStrategy()
.sendRedirect(request, response, "/login?erroroauth=userIsDisabled");
return;
HttpSession session = request.getSession(false);
if (session != null) {
String sessionId = session.getId();
sessionRegistry.removeSessionInformation(sessionId);
session.invalidate();
logger.debug("Session invalidated: " + sessionId);
}
getRedirectStrategy().sendRedirect(request, response, "/login?logout=true");
response.sendRedirect(request.getContextPath() + "/login?logout=true");
}
}

View File

@@ -3,6 +3,8 @@ package stirling.software.SPDF.config.security;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -15,6 +17,8 @@ public class LoginAttemptService {
@Autowired ApplicationProperties applicationProperties;
private static final Logger logger = LoggerFactory.getLogger(LoginAttemptService.class);
private int MAX_ATTEMPT;
private long ATTEMPT_INCREMENT_TIME;
private ConcurrentHashMap<String, AttemptCounter> attemptsCache;

View File

@@ -18,6 +18,8 @@ import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
@@ -35,7 +37,6 @@ import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationF
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2LogoutSuccessHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
@@ -46,7 +47,7 @@ import stirling.software.SPDF.model.provider.KeycloakProvider;
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
@Configuration
@EnableWebSecurity
@EnableWebSecurity()
@EnableMethodSecurity
public class SecurityConfiguration {
@@ -72,7 +73,11 @@ public class SecurityConfiguration {
@Autowired private LoginAttemptService loginAttemptService;
@Autowired private FirstLoginFilter firstLoginFilter;
@Autowired private SessionPersistentRegistry sessionRegistry;
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
@@ -89,7 +94,7 @@ public class SecurityConfiguration {
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(10)
.maxSessionsPreventsLogin(false)
.sessionRegistry(sessionRegistry)
.sessionRegistry(sessionRegistry())
.expiredUrl("/login?logout=true"));
http.formLogin(
@@ -98,7 +103,7 @@ public class SecurityConfiguration {
.loginPage("/login")
.successHandler(
new CustomAuthenticationSuccessHandler(
loginAttemptService, userService))
loginAttemptService))
.defaultSuccessUrl("/")
.failureHandler(
new CustomAuthenticationFailureHandler(
@@ -155,11 +160,7 @@ public class SecurityConfiguration {
// Handle OAUTH2 Logins
if (applicationProperties.getSecurity().getOAUTH2() != null
&& applicationProperties.getSecurity().getOAUTH2().getEnabled()
&& !applicationProperties
.getSecurity()
.getLoginMethod()
.equalsIgnoreCase("normal")) {
&& applicationProperties.getSecurity().getOAUTH2().getEnabled()) {
http.oauth2Login(
oauth2 ->
@@ -190,8 +191,10 @@ public class SecurityConfiguration {
.logout(
logout ->
logout.logoutSuccessHandler(
new CustomOAuth2LogoutSuccessHandler(
applicationProperties)));
new CustomOAuth2LogoutSuccessHandler(
this.applicationProperties,
sessionRegistry()))
.invalidateHttpSession(true));
}
} else {
http.csrf(csrf -> csrf.disable())

View File

@@ -1,7 +1,6 @@
package stirling.software.SPDF.config.security;
import java.io.IOException;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -10,9 +9,8 @@ import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -20,15 +18,14 @@ import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
@Component
public class UserAuthenticationFilter extends OncePerRequestFilter {
@Autowired @Lazy private UserService userService;
@Autowired private UserDetailsService userDetailsService;
@Autowired private SessionPersistentRegistry sessionPersistentRegistry;
@Autowired @Lazy private UserService userService;
@Autowired
@Qualifier("loginEnabled")
@@ -90,43 +87,6 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
}
}
// Check if the authenticated user is disabled and invalidate their session if so
if (authentication != null && authentication.isAuthenticated()) {
Object principal = authentication.getPrincipal();
String username = null;
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
username = ((OAuth2User) principal).getName();
} else if (principal instanceof String) {
username = (String) principal;
}
List<SessionInformation> sessionsInformations =
sessionPersistentRegistry.getAllSessions(principal, false);
if (username != null) {
boolean isUserExists = userService.usernameExistsIgnoreCase(username);
boolean isUserDisabled = userService.isUserDisabled(username);
if (!isUserExists || isUserDisabled) {
for (SessionInformation sessionsInformation : sessionsInformations) {
sessionsInformation.expireNow();
sessionPersistentRegistry.expireSession(sessionsInformation.getSessionId());
}
}
if (!isUserExists) {
response.sendRedirect(request.getContextPath() + "/logout?badcredentials=true");
return;
}
if (isUserDisabled) {
response.sendRedirect(request.getContextPath() + "/logout?userIsDisabled=true");
return;
}
}
}
filterChain.doFilter(request, response);
}

View File

@@ -15,15 +15,12 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import stirling.software.SPDF.config.DatabaseBackupInterface;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.model.Authority;
@@ -43,8 +40,6 @@ public class UserService implements UserServiceInterface {
@Autowired private MessageSource messageSource;
@Autowired private SessionPersistentRegistry sessionRegistry;
@Autowired DatabaseBackupInterface databaseBackupHelper;
// Handle OAUTH2 login and user auto creation.
@@ -53,7 +48,7 @@ public class UserService implements UserServiceInterface {
if (!isUsernameValid(username)) {
return false;
}
Optional<User> existingUser = findByUsernameIgnoreCase(username);
Optional<User> existingUser = userRepository.findByUsernameIgnoreCase(username);
if (existingUser.isPresent()) {
return true;
}
@@ -95,7 +90,8 @@ public class UserService implements UserServiceInterface {
public User addApiKeyToUser(String username) {
User user =
findByUsernameIgnoreCase(username)
userRepository
.findByUsernameIgnoreCase(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
user.setApiKey(generateApiKey());
@@ -108,7 +104,8 @@ public class UserService implements UserServiceInterface {
public String getApiKeyForUser(String username) {
User user =
findByUsernameIgnoreCase(username)
userRepository
.findByUsernameIgnoreCase(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return user.getApiKey();
}
@@ -134,17 +131,12 @@ public class UserService implements UserServiceInterface {
}
public boolean validateApiKeyForUser(String username, String apiKey) {
Optional<User> userOpt = findByUsernameIgnoreCase(username);
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
return userOpt.isPresent() && apiKey.equals(userOpt.get().getApiKey());
}
public void saveUser(String username, AuthenticationType authenticationType)
throws IllegalArgumentException, IOException {
saveUser(username, authenticationType, Role.USER.getRoleId());
}
public void saveUser(String username, AuthenticationType authenticationType, String role)
throws IllegalArgumentException, IOException {
if (!isUsernameValid(username)) {
throw new IllegalArgumentException(getInvalidUsernameMessage());
}
@@ -152,7 +144,7 @@ public class UserService implements UserServiceInterface {
user.setUsername(username);
user.setEnabled(true);
user.setFirstLogin(false);
user.addAuthority(new Authority(role, user));
user.addAuthority(new Authority(Role.USER.getRoleId(), user));
user.setAuthenticationType(authenticationType);
userRepository.save(user);
databaseBackupHelper.exportDatabase();
@@ -194,7 +186,7 @@ public class UserService implements UserServiceInterface {
}
public void deleteUser(String username) {
Optional<User> userOpt = findByUsernameIgnoreCase(username);
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
if (userOpt.isPresent()) {
for (Authority authority : userOpt.get().getAuthorities()) {
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
@@ -203,20 +195,21 @@ public class UserService implements UserServiceInterface {
}
userRepository.delete(userOpt.get());
}
invalidateUserSessions(username);
}
public boolean usernameExists(String username) {
return findByUsername(username).isPresent();
return userRepository.findByUsername(username).isPresent();
}
public boolean usernameExistsIgnoreCase(String username) {
return findByUsernameIgnoreCase(username).isPresent();
return userRepository.findByUsernameIgnoreCase(username).isPresent();
}
public boolean hasUsers() {
long userCount = userRepository.count();
if (findByUsernameIgnoreCase(Role.INTERNAL_API_USER.getRoleId()).isPresent()) {
if (userRepository
.findByUsernameIgnoreCase(Role.INTERNAL_API_USER.getRoleId())
.isPresent()) {
userCount -= 1;
}
return userCount > 0;
@@ -224,7 +217,7 @@ public class UserService implements UserServiceInterface {
public void updateUserSettings(String username, Map<String, String> updates)
throws IOException {
Optional<User> userOpt = findByUsernameIgnoreCase(username);
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
if (userOpt.isPresent()) {
User user = userOpt.get();
Map<String, String> settingsMap = user.getSettings();
@@ -275,17 +268,10 @@ public class UserService implements UserServiceInterface {
databaseBackupHelper.exportDatabase();
}
public void changeRole(User user, String newRole) throws IOException {
public void changeRole(User user, String newRole) {
Authority userAuthority = this.findRole(user);
userAuthority.setAuthority(newRole);
authorityRepository.save(userAuthority);
databaseBackupHelper.exportDatabase();
}
public void changeUserEnabled(User user, Boolean enbeled) throws IOException {
user.setEnabled(enbeled);
userRepository.save(user);
databaseBackupHelper.exportDatabase();
}
public boolean isPasswordCorrect(User user, String currentPassword) {
@@ -309,40 +295,14 @@ public class UserService implements UserServiceInterface {
}
public boolean hasPassword(String username) {
Optional<User> user = findByUsernameIgnoreCase(username);
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
return user.isPresent() && user.get().hasPassword();
}
public boolean isAuthenticationTypeByUsername(
String username, AuthenticationType authenticationType) {
Optional<User> user = findByUsernameIgnoreCase(username);
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
return user.isPresent()
&& authenticationType.name().equalsIgnoreCase(user.get().getAuthenticationType());
}
public boolean isUserDisabled(String username) {
Optional<User> userOpt = findByUsernameIgnoreCase(username);
return userOpt.map(user -> !user.isEnabled()).orElse(false);
}
public void invalidateUserSessions(String username) {
String usernameP = "";
for (Object principal : sessionRegistry.getAllPrincipals()) {
for (SessionInformation sessionsInformation :
sessionRegistry.getAllSessions(principal, false)) {
if (principal instanceof UserDetails) {
UserDetails userDetails = (UserDetails) principal;
usernameP = userDetails.getUsername();
} else if (principal instanceof OAuth2User) {
OAuth2User oAuth2User = (OAuth2User) principal;
usernameP = oAuth2User.getName();
} else if (principal instanceof String) {
usernameP = (String) principal;
}
if (usernameP.equalsIgnoreCase(username)) {
sessionRegistry.expireSession(sessionsInformation.getSessionId());
}
}
}
}
}

View File

@@ -8,7 +8,6 @@ import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
@@ -132,12 +131,11 @@ public class DatabaseBackupHelper implements DatabaseBackupInterface {
DateTimeFormatter myFormatObj = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
Path insertOutputFilePath =
this.getBackupFilePath("backup_" + dateNow.format(myFormatObj) + ".sql");
String query = "SCRIPT SIMPLE COLUMNS DROP to ?;";
String query = "SCRIPT SIMPLE COLUMNS DROP to '" + insertOutputFilePath.toString() + "';";
try (Connection conn = DriverManager.getConnection(url, "sa", "");
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setString(1, insertOutputFilePath.toString());
stmt.execute();
Statement stmt = conn.createStatement()) {
stmt.execute(query);
log.info("Database export completed: {}", insertOutputFilePath);
} catch (SQLException e) {
log.error("Error during database export: {}", e.getMessage(), e);
@@ -179,12 +177,11 @@ public class DatabaseBackupHelper implements DatabaseBackupInterface {
}
private boolean executeDatabaseScript(Path scriptPath) {
String query = "RUNSCRIPT from ?;";
try (Connection conn = DriverManager.getConnection(url, "sa", "");
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setString(1, scriptPath.toString());
stmt.execute();
Statement stmt = conn.createStatement()) {
String query = "RUNSCRIPT from '" + scriptPath.toString() + "';";
stmt.execute(query);
log.info("Database import completed: {}", scriptPath);
return true;
} catch (SQLException e) {

View File

@@ -2,8 +2,8 @@ package stirling.software.SPDF.config.security.oauth2;
import java.io.IOException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
@@ -13,34 +13,19 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CustomOAuth2AuthenticationFailureHandler
extends SimpleUrlAuthenticationFailureHandler {
private static final Logger logger =
LoggerFactory.getLogger(CustomOAuth2AuthenticationFailureHandler.class);
@Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
if (exception instanceof BadCredentialsException) {
log.error("BadCredentialsException", exception);
getRedirectStrategy().sendRedirect(request, response, "/login?error=badcredentials");
return;
}
if (exception instanceof DisabledException) {
log.error("User is deactivated: ", exception);
getRedirectStrategy().sendRedirect(request, response, "/logout?userIsDisabled=true");
return;
}
if (exception instanceof LockedException) {
log.error("Account locked: ", exception);
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
return;
}
if (exception instanceof OAuth2AuthenticationException) {
OAuth2Error error = ((OAuth2AuthenticationException) exception).getError();
@@ -49,13 +34,17 @@ public class CustomOAuth2AuthenticationFailureHandler
if (error.getErrorCode().equals("Password must not be null")) {
errorCode = "userAlreadyExistsWeb";
}
log.error("OAuth2 Authentication error: " + errorCode);
log.error("OAuth2AuthenticationException", exception);
logger.error("OAuth2 Authentication error: " + errorCode);
getRedirectStrategy()
.sendRedirect(request, response, "/logout?erroroauth=" + errorCode);
return;
} else if (exception instanceof LockedException) {
logger.error("Account locked: ", exception);
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
return;
} else {
logger.error("Unhandled authentication exception", exception);
super.onAuthenticationFailure(request, response, exception);
}
log.error("Unhandled authentication exception", exception);
super.onAuthenticationFailure(request, response, exception);
}
}

View File

@@ -2,9 +2,10 @@ package stirling.software.SPDF.config.security.oauth2;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.SavedRequest;
@@ -25,6 +26,9 @@ public class CustomOAuth2AuthenticationSuccessHandler
private LoginAttemptService loginAttemptService;
private static final Logger logger =
LoggerFactory.getLogger(CustomOAuth2AuthenticationSuccessHandler.class);
private ApplicationProperties applicationProperties;
private UserService userService;
@@ -42,17 +46,6 @@ public class CustomOAuth2AuthenticationSuccessHandler
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
Object principal = authentication.getPrincipal();
String username = "";
if (principal instanceof OAuth2User) {
OAuth2User oauthUser = (OAuth2User) principal;
username = oauthUser.getName();
} else if (principal instanceof UserDetails) {
UserDetails oauthUser = (UserDetails) principal;
username = oauthUser.getUsername();
}
// Get the saved request
HttpSession session = request.getSession(false);
String contextPath = request.getContextPath();
@@ -66,8 +59,11 @@ public class CustomOAuth2AuthenticationSuccessHandler
// Redirect to the original destination
super.onAuthenticationSuccess(request, response, authentication);
} else {
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
OAUTH2 oAuth = applicationProperties.getSecurity().getOAUTH2();
String username = oauthUser.getName();
if (loginAttemptService.isBlocked(username)) {
if (session != null) {
session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
@@ -82,21 +78,15 @@ public class CustomOAuth2AuthenticationSuccessHandler
&& oAuth.getAutoCreateUser()) {
response.sendRedirect(contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
return;
}
try {
if (oAuth.getBlockRegistration()
&& !userService.usernameExistsIgnoreCase(username)) {
response.sendRedirect(contextPath + "/logout?oauth2_admin_blocked_user=true");
} else {
try {
userService.processOAuth2PostLogin(username, oAuth.getAutoCreateUser());
response.sendRedirect(contextPath + "/");
return;
} catch (IllegalArgumentException e) {
response.sendRedirect(contextPath + "/logout?invalidUsername=true");
return;
}
if (principal instanceof OAuth2User) {
userService.processOAuth2PostLogin(username, oAuth.getAutoCreateUser());
}
response.sendRedirect(contextPath + "/");
return;
} catch (IllegalArgumentException e) {
response.sendRedirect(contextPath + "/logout?invalidUsername=true");
return;
}
}
}

View File

@@ -2,26 +2,34 @@ package stirling.software.SPDF.config.security.oauth2;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.Provider;
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
import stirling.software.SPDF.utils.UrlUtils;
@Slf4j
public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
private static final Logger logger =
LoggerFactory.getLogger(CustomOAuth2LogoutSuccessHandler.class);
private final SessionRegistry sessionRegistry;
private final ApplicationProperties applicationProperties;
public CustomOAuth2LogoutSuccessHandler(ApplicationProperties applicationProperties) {
public CustomOAuth2LogoutSuccessHandler(
ApplicationProperties applicationProperties, SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
this.applicationProperties = applicationProperties;
}
@@ -34,15 +42,6 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
String issuer = null;
String clientId = null;
if (authentication == null) {
if (request.getParameter("userIsDisabled") != null) {
response.sendRedirect(
request.getContextPath() + "/login?erroroauth=userIsDisabled");
} else {
super.onLogoutSuccess(request, response, authentication);
}
return;
}
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
if (authentication instanceof OAuth2AuthenticationToken) {
@@ -54,8 +53,9 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
issuer = provider.getIssuer();
clientId = provider.getClientId();
} catch (UnsupportedProviderException e) {
log.error(e.getMessage());
logger.error(e.getMessage());
}
} else {
registrationId = oauth.getProvider() != null ? oauth.getProvider() : "";
issuer = oauth.getIssuer();
@@ -70,16 +70,18 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
param = "erroroauth=" + sanitizeInput(errorMessage);
} else if (request.getParameter("oauth2AutoCreateDisabled") != null) {
param = "error=oauth2AutoCreateDisabled";
} else if (request.getParameter("oauth2_admin_blocked_user") != null) {
param = "erroroauth=oauth2_admin_blocked_user";
} else if (request.getParameter("userIsDisabled") != null) {
param = "erroroauth=userIsDisabled";
} else if (request.getParameter("badcredentials") != null) {
param = "error=badcredentials";
}
String redirect_url = UrlUtils.getOrigin(request) + "/login?" + param;
HttpSession session = request.getSession(false);
if (session != null) {
String sessionId = session.getId();
sessionRegistry.removeSessionInformation(sessionId);
session.invalidate();
logger.info("Session invalidated: " + sessionId);
}
switch (registrationId.toLowerCase()) {
case "keycloak":
// Add Keycloak specific logout URL if needed
@@ -90,13 +92,13 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
+ clientId
+ "&post_logout_redirect_uri="
+ response.encodeRedirectURL(redirect_url);
log.info("Redirecting to Keycloak logout URL: " + logoutUrl);
logger.info("Redirecting to Keycloak logout URL: " + logoutUrl);
response.sendRedirect(logoutUrl);
break;
case "github":
// Add GitHub specific logout URL if needed
String githubLogoutUrl = "https://github.com/logout";
log.info("Redirecting to GitHub logout URL: " + githubLogoutUrl);
logger.info("Redirecting to GitHub logout URL: " + githubLogoutUrl);
response.sendRedirect(githubLogoutUrl);
break;
case "google":
@@ -104,14 +106,13 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
// String googleLogoutUrl =
// "https://accounts.google.com/Logout?continue=https://appengine.google.com/_ah/logout?continue="
// + response.encodeRedirectURL(redirect_url);
log.info("Google does not have a specific logout URL");
// log.info("Redirecting to Google logout URL: " + googleLogoutUrl);
// logger.info("Redirecting to Google logout URL: " + googleLogoutUrl);
// response.sendRedirect(googleLogoutUrl);
// break;
default:
String defaultRedirectUrl = request.getContextPath() + "/login?" + param;
log.info("Redirecting to default logout URL: " + defaultRedirectUrl);
response.sendRedirect(defaultRedirectUrl);
String redirectUrl = request.getContextPath() + "/login?" + param;
logger.info("Redirecting to default logout URL: " + redirectUrl);
response.sendRedirect(redirectUrl);
break;
}
}

View File

@@ -1,26 +0,0 @@
package stirling.software.SPDF.config.security.session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class CustomHttpSessionListener implements HttpSessionListener {
@Autowired private SessionPersistentRegistry sessionPersistentRegistry;
@Override
public void sessionCreated(HttpSessionEvent se) {
log.info("Session created: " + se.getSession().getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
log.info("Session destroyed: " + se.getSession().getId());
sessionPersistentRegistry.expireSession(se.getSession().getId());
}
}

View File

@@ -1,183 +0,0 @@
package stirling.software.SPDF.config.security.session;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Component;
import jakarta.transaction.Transactional;
import stirling.software.SPDF.model.SessionEntity;
@Component
public class SessionPersistentRegistry implements SessionRegistry {
private final SessionRepository sessionRepository;
@Value("${server.servlet.session.timeout:30m}")
private Duration defaultMaxInactiveInterval;
public SessionPersistentRegistry(SessionRepository sessionRepository) {
this.sessionRepository = sessionRepository;
}
@Override
public List<Object> getAllPrincipals() {
List<SessionEntity> sessions = sessionRepository.findAll();
List<Object> principals = new ArrayList<>();
for (SessionEntity session : sessions) {
principals.add(session.getPrincipalName());
}
return principals;
}
@Override
public List<SessionInformation> getAllSessions(
Object principal, boolean includeExpiredSessions) {
List<SessionInformation> sessionInformations = new ArrayList<>();
String principalName = null;
if (principal instanceof UserDetails) {
principalName = ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
principalName = ((OAuth2User) principal).getName();
} else if (principal instanceof String) {
principalName = (String) principal;
}
if (principalName != null) {
List<SessionEntity> sessionEntities =
sessionRepository.findByPrincipalName(principalName);
for (SessionEntity sessionEntity : sessionEntities) {
if (includeExpiredSessions || !sessionEntity.isExpired()) {
sessionInformations.add(
new SessionInformation(
sessionEntity.getPrincipalName(),
sessionEntity.getSessionId(),
sessionEntity.getLastRequest()));
}
}
}
return sessionInformations;
}
@Override
@Transactional
public void registerNewSession(String sessionId, Object principal) {
String principalName = null;
if (principal instanceof UserDetails) {
principalName = ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
principalName = ((OAuth2User) principal).getName();
} else if (principal instanceof String) {
principalName = (String) principal;
}
if (principalName != null) {
SessionEntity sessionEntity = new SessionEntity();
sessionEntity.setSessionId(sessionId);
sessionEntity.setPrincipalName(principalName);
sessionEntity.setLastRequest(new Date()); // Set lastRequest to the current date
sessionEntity.setExpired(false);
sessionRepository.save(sessionEntity);
}
}
@Override
@Transactional
public void removeSessionInformation(String sessionId) {
sessionRepository.deleteById(sessionId);
}
@Override
@Transactional
public void refreshLastRequest(String sessionId) {
Optional<SessionEntity> sessionEntityOpt = sessionRepository.findById(sessionId);
if (sessionEntityOpt.isPresent()) {
SessionEntity sessionEntity = sessionEntityOpt.get();
sessionEntity.setLastRequest(new Date()); // Update lastRequest to the current date
sessionRepository.save(sessionEntity);
}
}
@Override
public SessionInformation getSessionInformation(String sessionId) {
Optional<SessionEntity> sessionEntityOpt = sessionRepository.findById(sessionId);
if (sessionEntityOpt.isPresent()) {
SessionEntity sessionEntity = sessionEntityOpt.get();
return new SessionInformation(
sessionEntity.getPrincipalName(),
sessionEntity.getSessionId(),
sessionEntity.getLastRequest());
}
return null;
}
// Retrieve all non-expired sessions
public List<SessionEntity> getAllSessionsNotExpired() {
return sessionRepository.findByExpired(false);
}
// Retrieve all sessions
public List<SessionEntity> getAllSessions() {
return sessionRepository.findAll();
}
// Mark a session as expired
public void expireSession(String sessionId) {
Optional<SessionEntity> sessionEntityOpt = sessionRepository.findById(sessionId);
if (sessionEntityOpt.isPresent()) {
SessionEntity sessionEntity = sessionEntityOpt.get();
sessionEntity.setExpired(true); // Set expired to true
sessionRepository.save(sessionEntity);
}
}
// Get the maximum inactive interval for sessions
public int getMaxInactiveInterval() {
return (int) defaultMaxInactiveInterval.getSeconds();
}
// Retrieve a session entity by session ID
public SessionEntity getSessionEntity(String sessionId) {
return sessionRepository.findBySessionId(sessionId);
}
// Update session details by principal name
public void updateSessionByPrincipalName(
String principalName, boolean expired, Date lastRequest) {
sessionRepository.saveByPrincipalName(expired, lastRequest, principalName);
}
// Find the latest session for a given principal name
public Optional<SessionEntity> findLatestSession(String principalName) {
List<SessionEntity> allSessions = sessionRepository.findByPrincipalName(principalName);
if (allSessions.isEmpty()) {
return Optional.empty();
}
// Sort sessions by lastRequest in descending order
Collections.sort(
allSessions,
new Comparator<SessionEntity>() {
@Override
public int compare(SessionEntity s1, SessionEntity s2) {
// Sort by lastRequest in descending order
return s2.getLastRequest().compareTo(s1.getLastRequest());
}
});
// The first session in the list is the latest session for the given principal name
return Optional.of(allSessions.get(0));
}
}

View File

@@ -1,20 +0,0 @@
package stirling.software.SPDF.config.security.session;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.session.SessionRegistryImpl;
@Configuration
public class SessionRegistryConfig {
@Bean
public SessionRegistryImpl sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public SessionPersistentRegistry sessionPersistentRegistry(
SessionRepository sessionRepository) {
return new SessionPersistentRegistry(sessionRepository);
}
}

View File

@@ -1,31 +0,0 @@
package stirling.software.SPDF.config.security.session;
import java.util.Date;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import jakarta.transaction.Transactional;
import stirling.software.SPDF.model.SessionEntity;
@Repository
public interface SessionRepository extends JpaRepository<SessionEntity, String> {
List<SessionEntity> findByPrincipalName(String principalName);
List<SessionEntity> findByExpired(boolean expired);
SessionEntity findBySessionId(String sessionId);
@Modifying
@Transactional
@Query(
"UPDATE SessionEntity s SET s.expired = :expired, s.lastRequest = :lastRequest WHERE s.principalName = :principalName")
void saveByPrincipalName(
@Param("expired") boolean expired,
@Param("lastRequest") Date lastRequest,
@Param("principalName") String principalName);
}

View File

@@ -1,35 +0,0 @@
package stirling.software.SPDF.config.security.session;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.stereotype.Component;
@Component
public class SessionScheduled {
@Autowired private SessionPersistentRegistry sessionPersistentRegistry;
@Scheduled(cron = "0 0/5 * * * ?")
public void expireSessions() {
Instant now = Instant.now();
for (Object principal : sessionPersistentRegistry.getAllPrincipals()) {
List<SessionInformation> sessionInformations =
sessionPersistentRegistry.getAllSessions(principal, false);
for (SessionInformation sessionInformation : sessionInformations) {
Date lastRequest = sessionInformation.getLastRequest();
int maxInactiveInterval = sessionPersistentRegistry.getMaxInactiveInterval();
Instant expirationTime =
lastRequest.toInstant().plus(maxInactiveInterval, ChronoUnit.SECONDS);
if (now.isAfter(expirationTime)) {
sessionPersistentRegistry.expireSession(sessionInformation.getSessionId());
}
}
}
}
}

View File

@@ -1,82 +0,0 @@
package stirling.software.SPDF.controller.api;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation;
import stirling.software.SPDF.model.api.PDFFile;
import stirling.software.SPDF.service.PdfImageRemovalService;
import stirling.software.SPDF.utils.WebResponseUtils;
/**
* Controller class for handling PDF image removal requests. Provides an endpoint to remove images
* from a PDF file to reduce its size.
*/
@RestController
@RequestMapping("/api/v1/general")
public class PdfImageRemovalController {
// Service for removing images from PDFs
@Autowired private PdfImageRemovalService pdfImageRemovalService;
/**
* Constructor for dependency injection of PdfImageRemovalService.
*
* @param pdfImageRemovalService The service used for removing images from PDFs.
*/
public PdfImageRemovalController(PdfImageRemovalService pdfImageRemovalService) {
this.pdfImageRemovalService = pdfImageRemovalService;
}
/**
* Endpoint to remove images from a PDF file.
*
* <p>This method processes the uploaded PDF file, removes all images, and returns the modified
* PDF file with a new name indicating that images were removed.
*
* @param file The PDF file with images to be removed.
* @return ResponseEntity containing the modified PDF file as byte array with appropriate
* content type and filename.
* @throws IOException If an error occurs while processing the PDF file.
*/
@PostMapping(consumes = "multipart/form-data", value = "/remove-image-pdf")
@Operation(
summary = "Remove images from file to reduce the file size.",
description =
"This endpoint remove images from file to reduce the file size.Input:PDF Output:PDF Type:MISO")
public ResponseEntity<byte[]> removeImages(@ModelAttribute PDFFile file) throws IOException {
MultipartFile pdf = file.getFileInput();
// Convert the MultipartFile to a byte array
byte[] pdfBytes = pdf.getBytes();
// Load the PDF document from the byte array
PDDocument document = Loader.loadPDF(pdfBytes);
// Remove images from the PDF document using the service
PDDocument modifiedDocument = pdfImageRemovalService.removeImagesFromPdf(document);
// Create a ByteArrayOutputStream to hold the modified PDF data
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Save the modified PDF document to the output stream
modifiedDocument.save(outputStream);
modifiedDocument.close();
// Generate a new filename for the modified PDF
String mergedFileName =
pdf.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_images.pdf";
// Convert the byte array to a web response and return it
return WebResponseUtils.bytesToWebResponse(outputStream.toByteArray(), mergedFileName);
}
}

View File

@@ -3,7 +3,6 @@ package stirling.software.SPDF.controller.api;
import java.io.IOException;
import java.security.Principal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -13,8 +12,8 @@ import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@@ -31,8 +30,6 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.model.Role;
import stirling.software.SPDF.model.User;
import stirling.software.SPDF.model.api.user.UsernameAndPass;
@@ -44,8 +41,6 @@ public class UserController {
@Autowired private UserService userService;
@Autowired SessionPersistentRegistry sessionRegistry;
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
@PostMapping("/register")
public String register(@ModelAttribute UsernameAndPass requestModel, Model model)
@@ -208,10 +203,9 @@ public class UserController {
@PreAuthorize("hasRole('ROLE_ADMIN')")
@PostMapping("/admin/saveUser")
public RedirectView saveUser(
@RequestParam String username,
@RequestParam(name = "password", required = false) String password,
@RequestParam(name = "username") String username,
@RequestParam(name = "password") String password,
@RequestParam(name = "role") String role,
@RequestParam(name = "authType") String authType,
@RequestParam(name = "forceChange", required = false, defaultValue = "false")
boolean forceChange)
throws IllegalArgumentException, IOException {
@@ -243,15 +237,7 @@ public class UserController {
return new RedirectView("/addUsers?messageType=invalidRole", true);
}
if (authType.equalsIgnoreCase(AuthenticationType.OAUTH2.toString())) {
userService.saveUser(username, AuthenticationType.OAUTH2, role);
} else {
if (password.isBlank()) {
return new RedirectView("/addUsers?messageType=invalidPassword", true);
}
userService.saveUser(username, password, role, forceChange);
}
userService.saveUser(username, password, role, forceChange);
return new RedirectView(
"/addUsers", true); // Redirect to account page after adding the user
}
@@ -261,8 +247,7 @@ public class UserController {
public RedirectView changeRole(
@RequestParam(name = "username") String username,
@RequestParam(name = "role") String role,
Authentication authentication)
throws IOException {
Authentication authentication) {
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
@@ -293,60 +278,6 @@ public class UserController {
User user = userOpt.get();
userService.changeRole(user, role);
return new RedirectView(
"/addUsers", true); // Redirect to account page after adding the user
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@PostMapping("/admin/changeUserEnabled/{username}")
public RedirectView changeUserEnabled(
@PathVariable("username") String username,
@RequestParam("enabled") boolean enabled,
Authentication authentication)
throws IOException {
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
if (!userOpt.isPresent()) {
return new RedirectView("/addUsers?messageType=userNotFound", true);
}
if (!userService.usernameExistsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=userNotFound", true);
}
// Get the currently authenticated username
String currentUsername = authentication.getName();
// Check if the provided username matches the current session's username
if (currentUsername.equalsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=disabledCurrentUser", true);
}
User user = userOpt.get();
userService.changeUserEnabled(user, enabled);
if (!enabled) {
// Invalidate all sessions if the user is being disabled
List<Object> principals = sessionRegistry.getAllPrincipals();
String userNameP = "";
for (Object principal : principals) {
List<SessionInformation> sessionsInformations =
sessionRegistry.getAllSessions(principal, false);
if (principal instanceof UserDetails) {
userNameP = ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
userNameP = ((OAuth2User) principal).getName();
} else if (principal instanceof String) {
userNameP = (String) principal;
}
if (userNameP.equalsIgnoreCase(username)) {
for (SessionInformation sessionsInformation : sessionsInformations) {
sessionRegistry.expireSession(sessionsInformation.getSessionId());
}
}
}
}
return new RedirectView(
"/addUsers", true); // Redirect to account page after adding the user
}
@@ -354,7 +285,7 @@ public class UserController {
@PreAuthorize("hasRole('ROLE_ADMIN')")
@PostMapping("/admin/deleteUser/{username}")
public RedirectView deleteUser(
@PathVariable("username") String username, Authentication authentication) {
@PathVariable(name = "username") String username, Authentication authentication) {
if (!userService.usernameExistsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=deleteUsernameExists", true);
@@ -367,18 +298,27 @@ public class UserController {
if (currentUsername.equalsIgnoreCase(username)) {
return new RedirectView("/addUsers?messageType=deleteCurrentUser", true);
}
// Invalidate all sessions before deleting the user
List<SessionInformation> sessionsInformations =
sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
for (SessionInformation sessionsInformation : sessionsInformations) {
sessionRegistry.expireSession(sessionsInformation.getSessionId());
sessionRegistry.removeSessionInformation(sessionsInformation.getSessionId());
}
invalidateUserSessions(username);
userService.deleteUser(username);
return new RedirectView("/addUsers", true);
}
@Autowired private SessionRegistry sessionRegistry;
private void invalidateUserSessions(String username) {
for (Object principal : sessionRegistry.getAllPrincipals()) {
if (principal instanceof UserDetails) {
UserDetails userDetails = (UserDetails) principal;
if (userDetails.getUsername().equals(username)) {
for (SessionInformation session :
sessionRegistry.getAllSessions(principal, false)) {
session.expireNow();
}
}
}
}
}
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
@PostMapping("/get-api-key")
public ResponseEntity<String> getApiKey(Principal principal) {

View File

@@ -39,12 +39,6 @@ public class ConvertWebsiteToPDF {
if (!URL.matches("^https?://.*") || !GeneralUtils.isValidURL(URL)) {
throw new IllegalArgumentException("Invalid URL format provided.");
}
// validate the URL is reachable
if (!GeneralUtils.isURLReachable(URL)) {
throw new IllegalArgumentException("URL is not reachable, please provide a valid URL.");
}
Path tempOutputFile = null;
byte[] pdfBytes;
try {

View File

@@ -1,12 +1,12 @@
package stirling.software.SPDF.controller.api.misc;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
@@ -17,7 +17,6 @@ import org.apache.pdfbox.text.PDFTextStripper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@@ -51,31 +50,31 @@ public class BlankPageController {
int threshold = request.getThreshold();
float whitePercent = request.getWhitePercent();
try (PDDocument document = Loader.loadPDF(inputFile.getBytes())) {
PDDocument document = null;
try {
document = Loader.loadPDF(inputFile.getBytes());
PDPageTree pages = document.getDocumentCatalog().getPages();
PDFTextStripper textStripper = new PDFTextStripper();
List<PDPage> nonBlankPages = new ArrayList<>();
List<PDPage> blankPages = new ArrayList<>();
List<Integer> pagesToKeepIndex = new ArrayList<>();
int pageIndex = 0;
PDFRenderer pdfRenderer = new PDFRenderer(document);
pdfRenderer.setSubsamplingAllowed(true);
for (PDPage page : pages) {
logger.info("checking page {}", pageIndex);
logger.info("checking page " + pageIndex);
textStripper.setStartPage(pageIndex + 1);
textStripper.setEndPage(pageIndex + 1);
String pageText = textStripper.getText(document);
boolean hasText = !pageText.trim().isEmpty();
boolean blank = true;
Boolean blank = true;
if (hasText) {
logger.info("page {} has text, not blank", pageIndex);
logger.info("page " + pageIndex + " has text, not blank");
blank = false;
} else {
boolean hasImages = PdfUtils.hasImagesOnPage(page);
if (hasImages) {
logger.info("page {} has image, running blank detection", pageIndex);
logger.info("page " + pageIndex + " has image, running blank detection");
// Render image and save as temp file
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 30);
blank = isBlankImage(image, threshold, whitePercent, threshold);
@@ -83,57 +82,34 @@ public class BlankPageController {
}
if (blank) {
logger.info("Skipping, Image was blank for page #{}", pageIndex);
blankPages.add(page);
logger.info("Skipping, Image was blank for page #" + pageIndex);
} else {
logger.info("page {} has image which is not blank", pageIndex);
nonBlankPages.add(page);
logger.info("page " + pageIndex + " has image which is not blank");
pagesToKeepIndex.add(pageIndex);
}
pageIndex++;
}
// Remove pages not present in pagesToKeepIndex
List<Integer> pageIndices =
IntStream.range(0, pages.getCount()).boxed().collect(Collectors.toList());
Collections.reverse(pageIndices); // Reverse to prevent index shifting during removal
for (Integer i : pageIndices) {
if (!pagesToKeepIndex.contains(i)) {
pages.remove(i);
}
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
String filename =
return WebResponseUtils.pdfDocToWebResponse(
document,
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
if (!nonBlankPages.isEmpty()) {
createZipEntry(zos, nonBlankPages, filename + "_nonBlankPages.pdf");
} else {
createZipEntry(zos, blankPages, filename + "_allBlankPages.pdf");
}
if (!nonBlankPages.isEmpty() && !blankPages.isEmpty()) {
createZipEntry(zos, blankPages, filename + "_blankPages.pdf");
}
zos.close();
logger.info("Returning ZIP file: {}", filename + "_processed.zip");
return WebResponseUtils.boasToWebResponse(
baos, filename + "_processed.zip", MediaType.APPLICATION_OCTET_STREAM);
.replaceFirst("[.][^.]+$", "")
+ "_blanksRemoved.pdf");
} catch (IOException e) {
logger.error("exception", e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
public void createZipEntry(ZipOutputStream zos, List<PDPage> pages, String entryName)
throws IOException {
try (PDDocument document = new PDDocument()) {
for (PDPage page : pages) {
document.addPage(page);
}
ZipEntry zipEntry = new ZipEntry(entryName);
zos.putNextEntry(zipEntry);
document.save(zos);
zos.closeEntry();
} finally {
if (document != null) document.close();
}
}

View File

@@ -99,7 +99,7 @@ public class CompressController {
List<String> command = new ArrayList<>();
command.add("gs");
command.add("-sDEVICE=pdfwrite");
command.add("-dCompatibilityLevel=1.5");
command.add("-dCompatibilityLevel=1.4");
switch (optimizeLevel) {
case 1:

View File

@@ -1,16 +1,13 @@
package stirling.software.SPDF.controller.api.misc;
import java.awt.*;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -50,19 +47,16 @@ public class ExtractImagesController {
@Operation(
summary = "Extract images from a PDF file",
description =
"This endpoint extracts images from a given PDF file and returns them in a zip file. Users can specify the output image format. Input: PDF Output: IMAGE/ZIP Type: SIMO")
"This endpoint extracts images from a given PDF file and returns them in a zip file. Users can specify the output image format. Input:PDF Output:IMAGE/ZIP Type:SIMO")
public ResponseEntity<byte[]> extractImages(@ModelAttribute PDFWithImageFormatRequest request)
throws IOException, InterruptedException, ExecutionException {
throws IOException {
MultipartFile file = request.getFileInput();
String format = request.getFormat();
System.out.println(
System.currentTimeMillis() + " file=" + file.getName() + ", format=" + format);
System.currentTimeMillis() + "file=" + file.getName() + ", format=" + format);
PDDocument document = Loader.loadPDF(file.getBytes());
// Determine if multithreading should be used based on PDF size or number of pages
boolean useMultithreading = shouldUseMultithreading(file, document);
// Create ByteArrayOutputStream to write zip file to byte array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -72,51 +66,71 @@ public class ExtractImagesController {
// Set compression level
zos.setLevel(Deflater.BEST_COMPRESSION);
int imageIndex = 1;
String filename =
Filenames.toSimpleFileName(file.getOriginalFilename())
.replaceFirst("[.][^.]+$", "");
int pageNum = 0;
Set<Integer> processedImages = new HashSet<>();
// Iterate over each page
for (PDPage page : document.getPages()) {
++pageNum;
// Extract images from page
for (COSName name : page.getResources().getXObjectNames()) {
if (page.getResources().isImageXObject(name)) {
PDImageXObject image = (PDImageXObject) page.getResources().getXObject(name);
int imageHash = image.hashCode();
if (processedImages.contains(imageHash)) {
continue; // Skip already processed images
}
processedImages.add(imageHash);
if (useMultithreading) {
// Executor service to handle multithreading
ExecutorService executor =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
Set<Future<Void>> futures = new HashSet<>();
// Convert image to desired format
RenderedImage renderedImage = image.getImage();
BufferedImage bufferedImage = null;
if ("png".equalsIgnoreCase(format)) {
bufferedImage =
new BufferedImage(
renderedImage.getWidth(),
renderedImage.getHeight(),
BufferedImage.TYPE_INT_ARGB);
} else if ("jpeg".equalsIgnoreCase(format) || "jpg".equalsIgnoreCase(format)) {
bufferedImage =
new BufferedImage(
renderedImage.getWidth(),
renderedImage.getHeight(),
BufferedImage.TYPE_INT_RGB);
} else if ("gif".equalsIgnoreCase(format)) {
bufferedImage =
new BufferedImage(
renderedImage.getWidth(),
renderedImage.getHeight(),
BufferedImage.TYPE_BYTE_INDEXED);
}
// Iterate over each page
for (int pgNum = 0; pgNum < document.getPages().getCount(); pgNum++) {
PDPage page = document.getPage(pgNum);
int pageNum = document.getPages().indexOf(page) + 1;
// Submit a task for processing each page
Future<Void> future =
executor.submit(
() -> {
extractImagesFromPage(
page, format, filename, pageNum, processedImages, zos);
return null;
});
// Write image to zip file
String imageName =
filename + "_" + imageIndex + " (Page " + pageNum + ")." + format;
ZipEntry zipEntry = new ZipEntry(imageName);
zos.putNextEntry(zipEntry);
futures.add(future);
}
Graphics2D g = bufferedImage.createGraphics();
g.drawImage((Image) renderedImage, 0, 0, null);
g.dispose();
// Write image bytes to zip file
ByteArrayOutputStream imageBaos = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, format, imageBaos);
zos.write(imageBaos.toByteArray());
// Wait for all tasks to complete
for (Future<Void> future : futures) {
future.get();
}
// Close executor service
executor.shutdown();
} else {
// Single-threaded extraction
for (int pgNum = 0; pgNum < document.getPages().getCount(); pgNum++) {
PDPage page = document.getPage(pgNum);
extractImagesFromPage(page, format, filename, pgNum + 1, processedImages, zos);
zos.closeEntry();
imageIndex++;
}
}
}
// Close PDDocument and ZipOutputStream
document.close();
// Close ZipOutputStream and PDDocument
zos.close();
document.close();
// Create ByteArrayResource from byte array
byte[] zipContents = baos.toByteArray();
@@ -124,69 +138,4 @@ public class ExtractImagesController {
return WebResponseUtils.boasToWebResponse(
baos, filename + "_extracted-images.zip", MediaType.APPLICATION_OCTET_STREAM);
}
private boolean shouldUseMultithreading(MultipartFile file, PDDocument document) {
// Criteria: Use multithreading if file size > 10MB or number of pages > 20
long fileSizeInMB = file.getSize() / (1024 * 1024);
int numberOfPages = document.getPages().getCount();
return fileSizeInMB > 10 || numberOfPages > 20;
}
private void extractImagesFromPage(
PDPage page,
String format,
String filename,
int pageNum,
Set<Integer> processedImages,
ZipOutputStream zos)
throws IOException {
for (COSName name : page.getResources().getXObjectNames()) {
if (page.getResources().isImageXObject(name)) {
PDImageXObject image = (PDImageXObject) page.getResources().getXObject(name);
int imageHash = image.hashCode();
synchronized (processedImages) {
if (processedImages.contains(imageHash)) {
continue; // Skip already processed images
}
processedImages.add(imageHash);
}
RenderedImage renderedImage = image.getImage();
// Convert to standard RGB colorspace if needed
BufferedImage bufferedImage = convertToRGB(renderedImage, format);
// Write image to zip file
String imageName = filename + "_" + imageHash + " (Page " + pageNum + ")." + format;
synchronized (zos) {
zos.putNextEntry(new ZipEntry(imageName));
ByteArrayOutputStream imageBaos = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, format, imageBaos);
zos.write(imageBaos.toByteArray());
zos.closeEntry();
}
}
}
}
private BufferedImage convertToRGB(RenderedImage renderedImage, String format) {
int width = renderedImage.getWidth();
int height = renderedImage.getHeight();
BufferedImage rgbImage;
if ("png".equalsIgnoreCase(format)) {
rgbImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
} else if ("jpeg".equalsIgnoreCase(format) || "jpg".equalsIgnoreCase(format)) {
rgbImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
} else if ("gif".equalsIgnoreCase(format)) {
rgbImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED);
} else {
rgbImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
}
Graphics2D g = rgbImage.createGraphics();
g.drawImage((Image) renderedImage, 0, 0, null);
g.dispose();
return rgbImage;
}
}

View File

@@ -13,7 +13,8 @@ import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
@@ -26,7 +27,6 @@ import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.api.misc.ProcessPdfWithOcrRequest;
import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
@@ -37,10 +37,10 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@Tag(name = "Misc", description = "Miscellaneous APIs")
public class OCRController {
@Autowired ApplicationProperties applicationProperties;
private static final Logger logger = LoggerFactory.getLogger(OCRController.class);
public List<String> getAvailableTesseractLanguages() {
String tessdataDir = applicationProperties.getSystem().getTessdataDir();
String tessdataDir = "/usr/share/tessdata";
File[] files = new File(tessdataDir).listFiles();
if (files == null) {
return Collections.emptyList();

View File

@@ -68,7 +68,6 @@ public class PasswordController {
boolean canModifyAnnotations = request.isCanModifyAnnotations();
boolean canPrint = request.isCanPrint();
boolean canPrintFaithful = request.isCanPrintFaithful();
PDDocument document = Loader.loadPDF(fileInput.getBytes());
AccessPermission ap = new AccessPermission();
ap.setCanAssembleDocument(!canAssembleDocument);

View File

@@ -1,14 +1,21 @@
package stirling.software.SPDF.controller.api.security;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
@@ -25,7 +32,6 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.PDFText;
import stirling.software.SPDF.model.api.security.RedactPdfRequest;
import stirling.software.SPDF.pdf.TextFinder;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
@@ -75,9 +81,22 @@ public class RedactController {
}
if (convertPDFToImage) {
PDDocument convertedPdf = PdfUtils.convertPdfToPdfImage(document);
PDDocument imageDocument = new PDDocument();
PDFRenderer pdfRenderer = new PDFRenderer(document);
pdfRenderer.setSubsamplingAllowed(true);
for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
PDPage newPage = new PDPage(new PDRectangle(bim.getWidth(), bim.getHeight()));
imageDocument.addPage(newPage);
PDImageXObject pdImage = LosslessFactory.createFromImage(imageDocument, bim);
PDPageContentStream contentStream =
new PDPageContentStream(
imageDocument, newPage, AppendMode.APPEND, true, true);
contentStream.drawImage(pdImage, 0, 0);
contentStream.close();
}
document.close();
document = convertedPdf;
document = imageDocument;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();

View File

@@ -36,7 +36,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.security.AddWatermarkRequest;
import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.WebResponseUtils;
@RestController
@@ -61,7 +60,6 @@ public class WatermarkController {
float opacity = request.getOpacity();
int widthSpacer = request.getWidthSpacer();
int heightSpacer = request.getHeightSpacer();
boolean convertPdfToImage = request.isConvertPDFToImage();
// Load the input PDF
PDDocument document = Loader.loadPDF(pdfFile.getBytes());
@@ -106,12 +104,6 @@ public class WatermarkController {
contentStream.close();
}
if (convertPdfToImage) {
PDDocument convertedPdf = PdfUtils.convertPdfToPdfImage(document);
document.close();
document = convertedPdf;
}
return WebResponseUtils.pdfDocToWebResponse(
document,
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())

View File

@@ -1,15 +1,13 @@
package stirling.software.SPDF.controller.web;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
@@ -25,14 +23,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
import stirling.software.SPDF.model.Authority;
import stirling.software.SPDF.model.Role;
import stirling.software.SPDF.model.SessionEntity;
import stirling.software.SPDF.model.User;
import stirling.software.SPDF.model.provider.GithubProvider;
import stirling.software.SPDF.model.provider.GoogleProvider;
@@ -40,20 +35,15 @@ import stirling.software.SPDF.model.provider.KeycloakProvider;
import stirling.software.SPDF.repository.UserRepository;
@Controller
@Slf4j
@Tag(name = "Account Security", description = "Account Security APIs")
public class AccountWebController {
@Autowired ApplicationProperties applicationProperties;
@Autowired SessionPersistentRegistry sessionPersistentRegistry;
@Autowired
private UserRepository userRepository; // Assuming you have a repository for user operations
private static final Logger logger = LoggerFactory.getLogger(AccountWebController.class);
@GetMapping("/login")
public String login(HttpServletRequest request, Model model, Authentication authentication) {
// If the user is already authenticated, redirect them to the home page.
if (authentication != null && authentication.isAuthenticated()) {
return "redirect:/";
}
@@ -147,13 +137,6 @@ public class AccountWebController {
break;
case "invalid_id_token":
erroroauth = "login.oauth2InvalidIdToken";
break;
case "oauth2_admin_blocked_user":
erroroauth = "login.oauth2AdminBlockedUser";
break;
case "userIsDisabled":
erroroauth = "login.userIsDisabled";
break;
default:
break;
}
@@ -172,6 +155,9 @@ public class AccountWebController {
return "login";
}
@Autowired
private UserRepository userRepository; // Assuming you have a repository for user operations
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/addUsers")
public String showAddUserForm(
@@ -180,13 +166,6 @@ public class AccountWebController {
Iterator<User> iterator = allUsers.iterator();
Map<String, String> roleDetails = Role.getAllRoleDetails();
// Map to store session information and user activity status
Map<String, Boolean> userSessions = new HashMap<>();
Map<String, Date> userLastRequest = new HashMap<>();
int activeUsers = 0;
int disabledUsers = 0;
while (iterator.hasNext()) {
User user = iterator.next();
if (user != null) {
@@ -197,73 +176,9 @@ public class AccountWebController {
break; // Break out of the inner loop once the user is removed
}
}
// Determine the user's session status and last request time
int maxInactiveInterval = sessionPersistentRegistry.getMaxInactiveInterval();
boolean hasActiveSession = false;
Date lastRequest = null;
Optional<SessionEntity> latestSession =
sessionPersistentRegistry.findLatestSession(user.getUsername());
if (latestSession.isPresent()) {
SessionEntity sessionEntity = latestSession.get();
Date lastAccessedTime = sessionEntity.getLastRequest();
Instant now = Instant.now();
// Calculate session expiration and update session status accordingly
Instant expirationTime =
lastAccessedTime
.toInstant()
.plus(maxInactiveInterval, ChronoUnit.SECONDS);
if (now.isAfter(expirationTime)) {
sessionPersistentRegistry.expireSession(sessionEntity.getSessionId());
hasActiveSession = false;
} else {
hasActiveSession = !sessionEntity.isExpired();
}
lastRequest = sessionEntity.getLastRequest();
} else {
hasActiveSession = false;
lastRequest = new Date(0); // No session, set default last request time
}
userSessions.put(user.getUsername(), hasActiveSession);
userLastRequest.put(user.getUsername(), lastRequest);
if (hasActiveSession) {
activeUsers++;
}
if (!user.isEnabled()) {
disabledUsers++;
}
}
}
// Sort users by active status and last request date
List<User> sortedUsers =
allUsers.stream()
.sorted(
(u1, u2) -> {
boolean u1Active = userSessions.get(u1.getUsername());
boolean u2Active = userSessions.get(u2.getUsername());
if (u1Active && !u2Active) {
return -1;
} else if (!u1Active && u2Active) {
return 1;
} else {
Date u1LastRequest =
userLastRequest.getOrDefault(
u1.getUsername(), new Date(0));
Date u2LastRequest =
userLastRequest.getOrDefault(
u2.getUsername(), new Date(0));
return u2LastRequest.compareTo(u1LastRequest);
}
})
.collect(Collectors.toList());
String messageType = request.getParameter("messageType");
String deleteMessage = null;
@@ -288,9 +203,6 @@ public class AccountWebController {
case "invalidUsername":
addMessage = "invalidUsernameMessage";
break;
case "invalidPassword":
addMessage = "invalidPasswordMessage";
break;
default:
break;
}
@@ -306,24 +218,16 @@ public class AccountWebController {
case "downgradeCurrentUser":
changeMessage = "downgradeCurrentUserMessage";
break;
case "disabledCurrentUser":
changeMessage = "disabledCurrentUserMessage";
break;
default:
changeMessage = messageType;
break;
}
model.addAttribute("changeMessage", changeMessage);
}
model.addAttribute("users", sortedUsers);
model.addAttribute("users", allUsers);
model.addAttribute("currentUsername", authentication.getName());
model.addAttribute("roleDetails", roleDetails);
model.addAttribute("userSessions", userSessions);
model.addAttribute("userLastRequest", userLastRequest);
model.addAttribute("totalUsers", allUsers.size());
model.addAttribute("activeUsers", activeUsers);
model.addAttribute("disabledUsers", disabledUsers);
return "addUsers";
}
@@ -374,7 +278,7 @@ public class AccountWebController {
settingsJson = objectMapper.writeValueAsString(user.get().getSettings());
} catch (JsonProcessingException e) {
// Handle JSON conversion error
log.error("exception", e);
logger.error("exception", e);
return "redirect:/error";
}

View File

@@ -310,11 +310,4 @@ public class GeneralWebController {
model.addAttribute("currentPage", "auto-split-pdf");
return "auto-split-pdf";
}
@GetMapping("/remove-image-pdf")
@Hidden
public String removeImagePdfForm(Model model) {
model.addAttribute("currentPage", "remove-image-pdf");
return "remove-image-pdf";
}
}

View File

@@ -6,7 +6,6 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@@ -15,14 +14,10 @@ import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.ApplicationProperties;
@Controller
@Tag(name = "Misc", description = "Miscellaneous APIs")
public class OtherWebController {
@Autowired ApplicationProperties applicationProperties;
@GetMapping("/compress-pdf")
@Hidden
public String compressPdfForm(Model model) {
@@ -102,7 +97,7 @@ public class OtherWebController {
}
public List<String> getAvailableTesseractLanguages() {
String tessdataDir = applicationProperties.getSystem().getTessdataDir();
String tessdataDir = "/usr/share/tessdata";
File[] files = new File(tessdataDir).listFiles();
if (files == null) {
return Collections.emptyList();

View File

@@ -241,7 +241,6 @@ public class ApplicationProperties {
private String clientId;
private String clientSecret;
private Boolean autoCreateUser = false;
private Boolean blockRegistration = false;
private String useAsUsername;
private Collection<String> scopes = new ArrayList<>();
private String provider;
@@ -287,14 +286,6 @@ public class ApplicationProperties {
this.autoCreateUser = autoCreateUser;
}
public Boolean getBlockRegistration() {
return blockRegistration;
}
public void setBlockRegistration(Boolean blockRegistration) {
this.blockRegistration = blockRegistration;
}
public String getUseAsUsername() {
return useAsUsername;
}
@@ -365,14 +356,10 @@ public class ApplicationProperties {
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
+ ", autoCreateUser="
+ autoCreateUser
+ ", blockRegistration="
+ blockRegistration
+ ", useAsUsername="
+ useAsUsername
+ ", provider="
+ provider
+ ", client="
+ client
+ ", scopes="
+ scopes
+ "]";
@@ -442,15 +429,6 @@ public class ApplicationProperties {
private boolean showUpdate;
private Boolean showUpdateOnlyAdmin;
private boolean customHTMLFiles;
private String tessdataDir;
public String getTessdataDir() {
return tessdataDir;
}
public void setTessdataDir(String tessdataDir) {
this.tessdataDir = tessdataDir;
}
public boolean isCustomHTMLFiles() {
return customHTMLFiles;

View File

@@ -1,23 +0,0 @@
package stirling.software.SPDF.model;
import java.io.Serializable;
import java.util.Date;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.Table;
import lombok.Data;
@Entity
@Data
@Table(name = "sessions")
public class SessionEntity implements Serializable {
@Id private String sessionId;
@Lob private String principalName;
private Date lastRequest;
private boolean expired;
}

View File

@@ -44,7 +44,4 @@ public class AddWatermarkRequest extends PDFFile {
@Schema(description = "The height spacer between watermark elements", example = "50")
private int heightSpacer;
@Schema(description = "Convert the redacted PDF to an image", defaultValue = "false")
private boolean convertPDFToImage;
}

View File

@@ -31,14 +31,6 @@ public class GoogleProvider extends Provider {
private Collection<String> scopes = new ArrayList<>();
private String useAsUsername = "email";
@Override
public String getIssuer() {
return new String();
}
@Override
public void setIssuer(String issuer) {}
@Override
public String getClientId() {
return this.clientId;

View File

@@ -3,11 +3,9 @@ package stirling.software.SPDF.repository;
import java.util.Set;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import stirling.software.SPDF.model.Authority;
@Repository
public interface AuthorityRepository extends JpaRepository<Authority, Long> {
// Set<Authority> findByUsername(String username);
Set<Authority> findByUser_Username(String username);

View File

@@ -1,9 +1,7 @@
package stirling.software.SPDF.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import stirling.software.SPDF.model.PersistentLogin;
@Repository
public interface PersistentLoginRepository extends JpaRepository<PersistentLogin, String> {}

View File

@@ -3,12 +3,10 @@ package stirling.software.SPDF.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import stirling.software.SPDF.model.User;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
public interface UserRepository extends JpaRepository<User, String> {
Optional<User> findByUsernameIgnoreCase(String username);
Optional<User> findByUsername(String username);

View File

@@ -1,41 +0,0 @@
package stirling.software.SPDF.service;
import java.io.IOException;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.springframework.stereotype.Service;
/** Service class responsible for removing image objects from a PDF document. */
@Service
public class PdfImageRemovalService {
/**
* Removes all image objects from the provided PDF document.
*
* This method iterates over each page in the document and removes any image XObjects found
* in the page's resources.
*
* @param document The PDF document from which images will be removed.
* @return The modified PDF document with images removed.
* @throws IOException If an error occurs while processing the PDF document.
*/
public PDDocument removeImagesFromPdf(PDDocument document) throws IOException {
// Iterate over each page in the PDF document
for (PDPage page : document.getPages()) {
PDResources resources = page.getResources();
// Iterate over all XObject names in the page's resources
for (COSName name : resources.getXObjectNames()) {
// Check if the XObject is an image
if (resources.isImageXObject(name)) {
// Remove the image XObject by setting it to null
resources.put(name, (PDXObject) null);
}
}
}
return document;
}
}

View File

@@ -13,8 +13,6 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.net.URL;
import java.net.HttpURLConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -73,21 +71,6 @@ public class GeneralUtils {
} catch (MalformedURLException e) {
return false;
}
}
public static boolean isURLReachable(String urlStr) {
try {
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("HEAD");
int responseCode = connection.getResponseCode();
return (200 <= responseCode && responseCode <= 399);
} catch (MalformedURLException e) {
return false;
} catch (IOException e) {
return false;
}
}
public static File multipartToFile(MultipartFile multipart) throws IOException {
@@ -112,16 +95,19 @@ public class GeneralUtils {
sizeStr = sizeStr.replace(",", ".").replace(" ", "");
try {
if (sizeStr.endsWith("KB")) {
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024);
return (long)
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024);
} else if (sizeStr.endsWith("MB")) {
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024);
return (long)
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024);
} else if (sizeStr.endsWith("GB")) {
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024
* 1024);
return (long)
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024
* 1024);
} else if (sizeStr.endsWith("B")) {
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
} else {
@@ -184,15 +170,13 @@ public class GeneralUtils {
int n = 0;
while (true) {
// Replace 'n' with the current value of n, correctly handling numbers before
// 'n'
// Replace 'n' with the current value of n, correctly handling numbers before 'n'
String sanitizedExpression = insertMultiplicationBeforeN(expression, n);
Double result = evaluator.evaluate(sanitizedExpression);
// Check if the result is null or not within bounds
if (result == null || result <= 0 || result.intValue() > maxValue) {
if (n != 0)
break;
if (n != 0) break;
} else {
results.add(result.intValue());
}

View File

@@ -341,30 +341,6 @@ public class PdfUtils {
}
}
/**
* Converts a given Pdf file to PDF-Image.
*
* @param document to be converted. Note: the caller is responsible for closing the document
* @return converted document to PDF-Image
* @throws IOException if conversion fails
*/
public static PDDocument convertPdfToPdfImage(PDDocument document) throws IOException {
PDDocument imageDocument = new PDDocument();
PDFRenderer pdfRenderer = new PDFRenderer(document);
pdfRenderer.setSubsamplingAllowed(true);
for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
PDPage newPage = new PDPage(new PDRectangle(bim.getWidth(), bim.getHeight()));
imageDocument.addPage(newPage);
PDImageXObject pdImage = LosslessFactory.createFromImage(imageDocument, bim);
PDPageContentStream contentStream =
new PDPageContentStream(imageDocument, newPage, AppendMode.APPEND, true, true);
contentStream.drawImage(pdImage, 0, 0);
contentStream.close();
}
return imageDocument;
}
private static BufferedImage prepareImageForPdfToImage(
int maxWidth, int height, String imageType) {
BufferedImage combined;

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=لا يمكن خفض دور المستخدم الحالي
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=لا يمكن تخفيض دور المستخدم الحالي. وبالتالي، لن يظهر المستخدم الحالي.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
@@ -179,7 +177,6 @@ adminUserSettings.user=User
adminUserSettings.addUser=Add New User
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles
adminUserSettings.role=Role
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=تغيير دور المستخدم
adminUserSettings.authenticated=Authenticated
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=تسجيل الدخول عبر تسجيل الدخول الأحادي
login.oauth2AutoCreateDisabled=تم تعطيل مستخدم الإنشاء التلقائي لـ OAuth2
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=heightSpacer (مسافة بين كل علامة مائي
watermark.selectText.7=التعتيم (0٪ - 100٪):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=إضافة علامة مائية
watermark.type.1=نص
watermark.type.2=صورة
@@ -1142,9 +1125,3 @@ error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=Потребителят не е намерен
incorrectPasswordMessage=Текущата парола е неправилна.
usernameExistsMessage=Новият потребител вече съществува.
invalidUsernameMessage=Невалидно потребителско име, потребителското име може да съдържа само букви, цифри и следните специални знаци @._+- или трябва да е валиден имейл адрес.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Не може да се изтрие вписания в момента потребител.
deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито.
downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан.
userAlreadyExistsOAuthMessage=Потребителят вече съществува като OAuth2 потребител.
userAlreadyExistsWebMessage=Потребителят вече съществува като уеб-потребител.
@@ -179,7 +177,6 @@ adminUserSettings.user=Потребител
adminUserSettings.addUser=Добавяне на нов потребител
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Потребителското име може да съдържа само букви, цифри и следните специални символи @._+- или трябва да е валиден имейл адрес.
adminUserSettings.roles=Роли
adminUserSettings.role=Роля
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Принудете потребителя да п
adminUserSettings.submit=Съхранете потребителя
adminUserSettings.changeUserRole=Промяна на ролята на потребителя
adminUserSettings.authenticated=Удостоверен
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Книга към PDF
home.BookToPDF.desc=Преобразува формати на книги/комикси в PDF с помощта на calibre
BookToPDF.tags=Книга,комикс,calibre,конвертиране,манга,Amazon,Kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Вашият акаунт е заключен.
login.signinTitle=Моля впишете се
login.ssoSignIn=Влизане чрез еднократно влизане
login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Заявката за оторизация не е намерена
login.oauth2InvalidUserInfoResponse=Невалидна информация за потребителя
login.oauth2invalidRequest=Невалидна заявка
login.oauth2AccessDenied=Отказан достъп
login.oauth2InvalidTokenResponse=Невалиден отговор на токена
login.oauth2InvalidIdToken=Невалиден токен за идентификатор
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=дължинаSpacer (Разстояние между в
watermark.selectText.7=Непрозрачност (0% - 100%):
watermark.selectText.8=Тип воден знак:
watermark.selectText.9=Изображение за воден знак:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Добавяне на воден знак
watermark.type.1=Текст
watermark.type.2=Изображение
@@ -1142,9 +1125,3 @@ error.copyStack=Копиране на проследяване на стека
error.githubSubmit=GitHub - Изпратете запитване
error.discordSubmit=Discord - Изпратете запитване за поддръжка
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=No es pot reduir la funció de l'usuari actual
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=No es pot baixar la funció de l'usuari actual. Per tant, no es mostrarà l'usuari actual.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
@@ -179,7 +177,6 @@ adminUserSettings.user=Usuari
adminUserSettings.addUser=Afegir Usuari
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Rols
adminUserSettings.role=Rol
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Desar Usuari
adminUserSettings.changeUserRole=Canvia el rol de l'usuari
adminUserSettings.authenticated=Authenticated
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Compte bloquejat
login.signinTitle=Autenticat
login.ssoSignIn=Inicia sessió mitjançant l'inici de sessió ún
login.oauth2AutoCreateDisabled=L'usuari de creació automàtica OAUTH2 està desactivat
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=separació d'alçada (Espai vertical entre cada Marca d'A
watermark.selectText.7=Opacitat (0% - 100%):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Afegir Marca d'Aigua
watermark.type.1=Text
watermark.type.2=Image
@@ -1142,9 +1125,3 @@ error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=Uživatel nenalezen.
incorrectPasswordMessage=Současné heslo není správné.
usernameExistsMessage=Nové uživatelské jméno již existuje.
invalidUsernameMessage=Nesprávné uživatelské jméno, smí obsahovat pouze písmena, číslice a následující speciální znaky @._+- nebo musí být validní emailová adresa.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Nelze smazat aktuální přihlášeného uživatele.
deleteUsernameExistsMessage=Uživatelské jméno neexistuje a nelze ho smazat.
downgradeCurrentUserMessage=Nelze snížit roli aktuálního uživatele.
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Nelze snížit roli aktuálního uživatele. Proto nebude aktuální uživatel zobrazen.
userAlreadyExistsOAuthMessage=Uživatel již existuje jako OAuth2 uživatel.
userAlreadyExistsWebMessage=Uživatel již existuje jako webový uživatel.
@@ -179,7 +177,6 @@ adminUserSettings.user=Uživatel
adminUserSettings.addUser=Přidat Nového Uživatele
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Uživatelské Jméno může obsahovat pouze písmena, čísla a následující speciální znaky @._+- nebo musí být správná emailová adresa.
adminUserSettings.roles=Role
adminUserSettings.role=Role
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Vynutit uživateli změnu hesla při přihlášen
adminUserSettings.submit=Uložit Uživatele
adminUserSettings.changeUserRole=Zmenit Roli Uživatele
adminUserSettings.authenticated=Ověřeno
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Kniha na PDF
home.BookToPDF.desc=Převádí formáty knih/komiksů do PDF pomocí calibre
BookToPDF.tags=Kniha,Komiks,Calibre,Konvertovat,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=Login via Single Sign-on
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=Výška mezery (Mezera mezi každým vodoznakem svisle):
watermark.selectText.7=Průhlednost (0% - 100%):
watermark.selectText.8=Typ vodoznaku:
watermark.selectText.9=Obrázek vodoznaku:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Přidat vodoznak
watermark.type.1=Text
watermark.type.2=Obrázek
@@ -1142,9 +1125,3 @@ error.copyStack=Kopírovat stopu zásobníku
error.githubSubmit=GitHub - Odeslat požadavek
error.discordSubmit=Discord - Odeslat příspěvek podpory
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

File diff suppressed because it is too large Load Diff

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=Benutzer nicht gefunden.
incorrectPasswordMessage=Das Passwort ist falsch.
usernameExistsMessage=Neuer Benutzername existiert bereits.
invalidUsernameMessage=Ungültiger Benutzername. Der Benutzername darf nur Buchstaben, Zahlen und die folgenden Sonderzeichen @._+- enthalten oder muss eine gültige E-Mail-Adresse sein.
invalidPasswordMessage=Das Passwort darf nicht leer sein und kein Leerzeichen am Anfang und Ende haben.
confirmPasswordErrorMessage=„Neues Passwort“ und „Neues Passwort bestätigen“ müssen übereinstimmen.
deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht werden.
deleteUsernameExistsMessage=Der Benutzername existiert nicht und kann nicht gelöscht werden.
downgradeCurrentUserMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden
disabledCurrentUserMessage=Der aktuelle Benutzer kann nicht deaktiviert werden
downgradeCurrentUserLongMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden. Daher wird der aktuelle Benutzer nicht angezeigt.
userAlreadyExistsOAuthMessage=Der Benutzer ist bereits als OAuth2-Benutzer vorhanden.
userAlreadyExistsWebMessage=Der Benutzer ist bereits als Webbenutzer vorhanden.
@@ -179,11 +177,10 @@ adminUserSettings.user=Benutzer
adminUserSettings.addUser=Neuen Benutzer hinzufügen
adminUserSettings.deleteUser=Benutzer löschen
adminUserSettings.confirmDeleteUser=Soll der Benutzer gelöscht werden?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Der Benutzername darf nur Buchstaben, Zahlen und die folgenden Sonderzeichen @._+- enthalten oder muss eine gültige E-Mail-Adresse sein.
adminUserSettings.roles=Rollen
adminUserSettings.role=Rolle
adminUserSettings.actions=Aktions
adminUserSettings.actions=Aktion
adminUserSettings.apiUser=Eingeschränkter API-Benutzer
adminUserSettings.extraApiUser=Zusätzlicher eingeschränkter API-Benutzer
adminUserSettings.webOnlyUser=Nur Web-Benutzer
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei
adminUserSettings.submit=Benutzer speichern
adminUserSettings.changeUserRole=Benutzerrolle ändern
adminUserSettings.authenticated=Authentifiziert
adminUserSettings.editOwnProfil=Eigenes Profil bearbeiten
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Datenbank Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Buch als PDF
home.BookToPDF.desc=Konvertiert Buch-/Comic-Formate mithilfe von Calibre in PDF
BookToPDF.tags=buch,comic,calibre,convert,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Ihr Konto wurde gesperrt.
login.signinTitle=Bitte melden Sie sich an.
login.ssoSignIn=Anmeldung per Single Sign-On
login.oauth2AutoCreateDisabled=OAUTH2 Benutzer automatisch erstellen deaktiviert
login.oauth2AdminBlockedUser=Die Registrierung bzw. das anmelden von nicht registrierten Benutzern ist derzeit gesperrt. Bitte wenden Sie sich an den Administrator.
login.oauth2RequestNotFound=Autorisierungsanfrage nicht gefunden
login.oauth2InvalidUserInfoResponse=Ungültige Benutzerinformationsantwort
login.oauth2invalidRequest=ungültige Anfrage
login.oauth2AccessDenied=Zugriff abgelehnt
login.oauth2InvalidTokenResponse=Ungültige Token-Antwort
login.oauth2InvalidIdToken=Ungültiges ID-Token
login.userIsDisabled=Benutzer ist deaktiviert, die Anmeldung ist mit diesem Benutzernamen derzeit gesperrt. Bitte wenden Sie sich an den Administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=höheSpacer (vertikaler Abstand zwischen den einzelnen Wa
watermark.selectText.7=Deckkraft (0% - 100 %):
watermark.selectText.8=Wasserzeichen Typ:
watermark.selectText.9=Wasserzeichen-Bild:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Wasserzeichen hinzufügen
watermark.type.1=Text
watermark.type.2=Bild
@@ -1142,9 +1125,3 @@ error.copyStack=Stack-Trace kopieren
error.githubSubmit=GitHub - Ein Ticket einreichen
error.discordSubmit=Discord - Unterstützungsbeitrag einreichen
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=Ο χρήστης δεν βρέθηκε.
incorrectPasswordMessage=Ο τρέχων κωδικός πρόσβασης είναι λανθασμένος.
usernameExistsMessage=Το νέο όνομα χρήστη υπάρχει ήδη.
invalidUsernameMessage=Μη έγκυρο όνομα χρήστη, όνομα χρήστη μπορεί να περιέχει μόνο γράμματα, αριθμούς και τους ακόλουθους ειδικούς χαρακτήρες @._+- ή πρέπει να είναι έγκυρη διεύθυνση email.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Δεν είναι δυνατή η διαγραφή του τρέχοντος συνδεδεμένου χρήστη.
deleteUsernameExistsMessage=Το όνομα χρήστη δεν υπάρχει και δεν μπορεί να διαγραφεί.
downgradeCurrentUserMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη. Ως εκ τούτου, ο τρέχων χρήστης δεν θα εμφανίζεται.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
@@ -179,7 +177,6 @@ adminUserSettings.user=Χρήστης
adminUserSettings.addUser=Προσθήκη νέου Χρήστη
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Ρόλοι
adminUserSettings.role=Ρόλος
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Αναγκάστε τον χρήστη να αλλ
adminUserSettings.submit=Αποθήκευση Χρήστη
adminUserSettings.changeUserRole=Αλλαγή ρόλου χρήστη
adminUserSettings.authenticated=Authenticated
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Book σε PDF
home.BookToPDF.desc=Μετατρέπει τις μορφές Books/Comics σε PDF χρησιμοποιώντας calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Ο λογαριασμός σας έχει κλειδωθεί.
login.signinTitle=Παρακαλώ, συνδεθείτε
login.ssoSignIn=Σύνδεση μέσω μοναδικής σύνδεσης
login.oauth2AutoCreateDisabled=Απενεργοποιήθηκε ο χρήστης αυτόματης δημιουργίας OAUTH2
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=heightSpacer (Κενό μεταξύ κάθε υδατογ
watermark.selectText.7=Αδιαφάνεια (Opacity) (0% - 100%):
watermark.selectText.8=Τύπος Υδατογραφήματος:
watermark.selectText.9=Εικόνα Υδατογραφήματος:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Προσθήκη Υδατογραφήματος
watermark.type.1=Κείμενο
watermark.type.2=Εικόνα
@@ -1142,9 +1125,3 @@ error.copyStack=Αντιγραφή Stack Trace
error.githubSubmit=GitHub - Υποβάλετε ένα ticket
error.discordSubmit=Discord - Υποβάλετε ένα Support post
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Cannot downgrade current user's role
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
@@ -142,7 +140,7 @@ settings.cacheInputs.help=Enable to store previously used inputs for future runs
changeCreds.title=Change Credentials
changeCreds.header=Update Your Account Details
changeCreds.changePassword=You are using default login credentials. Please enter a new password
changeCreds.changePassword=You are using default login credentials. Please enter a new password
changeCreds.newUsername=New Username
changeCreds.oldPassword=Current Password
changeCreds.newPassword=New Password
@@ -179,7 +177,6 @@ adminUserSettings.user=User
adminUserSettings.addUser=Add New User
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles
adminUserSettings.role=Role
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=Change User's Role
adminUserSettings.authenticated=Authenticated
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=Login via Single Sign-on
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -722,8 +706,8 @@ removeAnnotations.submit=Remove
#compare
compare.title=Compare
compare.header=Compare PDFs
compare.highlightColor.1=Highlight Color 1:
compare.highlightColor.2=Highlight Color 2:
compare.highlightColor.1=Highlight Color 1:
compare.highlightColor.2=Highlight Color 2:
compare.document.1=Document 1
compare.document.2=Document 2
compare.submit=Compare
@@ -760,7 +744,7 @@ repair.submit=Repair
#flatten
flatten.title=Flatten
flatten.header=Flatten PDF
flatten.flattenOnlyForms=Flatten only forms
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=Flatten
@@ -961,7 +945,6 @@ watermark.selectText.6=heightSpacer (Space between each watermark vertically):
watermark.selectText.7=Opacity (0% - 100%):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Add Watermark
watermark.type.1=Text
watermark.type.2=Image
@@ -1142,9 +1125,3 @@ error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Cannot downgrade current user's role
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
@@ -179,7 +177,6 @@ adminUserSettings.user=User
adminUserSettings.addUser=Add New User
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles
adminUserSettings.role=Role
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=Change User's Role
adminUserSettings.authenticated=Authenticated
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Your account has been locked.
login.signinTitle=Please sign in
login.ssoSignIn=Login via Single Sign-on
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -722,8 +706,8 @@ removeAnnotations.submit=Remove
#compare
compare.title=Compare
compare.header=Compare PDFs
compare.highlightColor.1=Highlight Color 1:
compare.highlightColor.2=Highlight Color 2:
compare.highlightColor.1=Highlight Color 1:
compare.highlightColor.2=Highlight Color 2:
compare.document.1=Document 1
compare.document.2=Document 2
compare.submit=Compare
@@ -961,7 +945,6 @@ watermark.selectText.6=heightSpacer (Space between each watermark vertically):
watermark.selectText.7=Opacity (0% - 100%):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Add Watermark
watermark.type.1=Text
watermark.type.2=Image
@@ -1142,9 +1125,3 @@ error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,18 +55,16 @@ userNotFoundMessage=Usuario no encontrado.
incorrectPasswordMessage=La contraseña actual no es correcta.
usernameExistsMessage=El nuevo nombre de usuario está en uso.
invalidUsernameMessage=Nombre de usuario no válido, el nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=No puede eliminar el usuario que tiene la sesión actualmente en uso.
deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse.
downgradeCurrentUserMessage=No se puede degradar el rol del usuario actual
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=No se puede degradar el rol del usuario actual. Por lo tanto, el usuario actual no se mostrará.
userAlreadyExistsOAuthMessage=La usuario ya existe como usuario de OAuth2.
userAlreadyExistsWebMessage=El usuario ya existe como usuario web.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error
oops=Ups!
help=Ayuda
help=Help
goHomepage=Ir a la página principal
joinDiscord=Únase a nuestro servidor Discord
seeDockerHub=Ver Docker Hub
@@ -88,7 +86,7 @@ pipeline.defaultOption=Personalizar
pipeline.submitButton=Enviar
pipeline.help=Ayuda de Canalización
pipeline.scanHelp=Ayuda de escaneado de carpetas
pipeline.deletePrompt=¿Estás segura de que quieres eliminar la canalización?
pipeline.deletePrompt=Are you sure you want to delete pipeline
######################
# Pipeline Options #
@@ -109,18 +107,18 @@ pipelineOptions.validateButton=Validar
#############
# NAVBAR #
#############
navbar.favorite=Favoritos
navbar.favorite=Favorites
navbar.darkmode=Modo oscuro
navbar.language=Idiomas
navbar.language=Languages
navbar.settings=Configuración
navbar.allTools=Herramientas
navbar.multiTool=Multi herramientas
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convertir a PDF
navbar.sections.convertFrom=Convertir desde PDF
navbar.sections.security=Señalización y seguridad
navbar.sections.advance=Avanzado
navbar.sections.edit=Ver y Editar
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
#############
# SETTINGS #
@@ -177,9 +175,8 @@ adminUserSettings.header=Configuración de control de usuario administrador
adminUserSettings.admin=Administrador
adminUserSettings.user=Usuario
adminUserSettings.addUser=Añadir Nuevo Usuario
adminUserSettings.deleteUser=Eliminar Usuario
adminUserSettings.confirmDeleteUser=¿Se debe eliminar al usuario?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.usernameInfo=El nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida.
adminUserSettings.roles=Roles
adminUserSettings.role=Rol
@@ -192,31 +189,24 @@ adminUserSettings.internalApiUser=Usuario interno de API
adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso
adminUserSettings.submit=Guardar Usuario
adminUserSettings.changeUserRole=Cambiar rol de usuario
adminUserSettings.authenticated=Autenticado
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
adminUserSettings.authenticated=Authenticated
database.title=Base de Datos Importar/Exportar
database.header=Base de Datos Importar/Exportar
database.fileName=Nombre de Archivo
database.creationDate=Fecha de creación
database.fileSize=Tamaño de archivo
database.deleteBackupFile=Eliminar archivo de copia de seguridad
database.importBackupFile=Importar archivo de copia de seguridad
database.downloadBackupFile=Descargar archivo de copia de seguridad
database.info_1=Al importar datos, es fundamental garantizar la estructura correcta. Si no está seguro de lo que está haciendo, busque consejo y apoyo de un profesional. Un error en la estructura puede causar un mal funcionamiento de la aplicación, incluyendo la imposibilidad total de ejecutar la aplicación.
database.info_2=El nombre del archivo no importa al cargarlo. Posteriormente se le cambiará el nombre para que siga el formato backup_user_yyyyMMddHHmm.sql, lo que garantiza una convención de nomenclatura coherente.
database.submit=Importar Backup
database.importIntoDatabaseSuccessed=Importación a la base de datos ha sido exitosa
database.fileNotFound=Archivo no encontrado
database.fileNullOrEmpty=El archivo no debe ser nulo o vacío.
database.failedImportFile=Archivo de importación fallido
database.title=Database Import/Export
database.header=Database Import/Export
database.fileName=File Name
database.creationDate=Creation Date
database.fileSize=File Size
database.deleteBackupFile=Delete Backup File
database.importBackupFile=Import Backup File
database.downloadBackupFile=Download Backup File
database.info_1=When importing data, it is crucial to ensure the correct structure. If you are unsure of what you are doing, seek advice and support from a professional. An error in the structure can cause application malfunctions, up to and including the complete inability to run the application.
database.info_2=The file name does not matter when uploading. It will be renamed afterward to follow the format backup_user_yyyyMMddHHmm.sql, ensuring a consistent naming convention.
database.submit=Import Backup
database.importIntoDatabaseSuccessed=Import into database successed
database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
#############
# HOME-PAGE #
@@ -363,8 +353,8 @@ home.certSign.title=Firmar con certificado
home.certSign.desc=Firmar un PDF con un Certificado/Clave (PEM/P12)
certSign.tags=autentificar,PEM,P12,oficial,encriptar
home.removeCertSign.title=Quitar signo de certificado
home.removeCertSign.desc=Eliminar firma de certificado de PDF
home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Diseño de varias páginas
@@ -471,10 +461,6 @@ home.BookToPDF.title=Libro a PDF
home.BookToPDF.desc=Convierte formatos de Libro/Cómic a PDF usando Calibre
BookToPDF.tags=Libro,Cómic,Calibre,Convertir,manga,Amazon,Kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -490,15 +476,13 @@ login.invalid=Nombre de usuario o contraseña erróneos.
login.locked=Su cuenta se ha bloqueado.
login.signinTitle=Por favor, inicie sesión
login.ssoSignIn=Iniciar sesión a través del inicio de sesión único
login.oauth2AutoCreateDisabled=Usuario de creación automática de OAUTH2 DESACTIVADO
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Solicitud de autorización no encontrada
login.oauth2InvalidUserInfoResponse=Respuesta de información de usuario no válida
login.oauth2AutoCreateDisabled=Usuario DE creación automática de OAUTH2 DESACTIVADO
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Respuesta de token no válida
login.oauth2InvalidIdToken=Token de identificación no válido
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact
@@ -697,10 +681,10 @@ certSign.submit=Firmar PDF
#removeCertSign
removeCertSign.title=Eliminar firma del certificado
removeCertSign.header=Quitar el certificado digital del PDF
removeCertSign.selectPDF=Seleccione un archivo PDF:
removeCertSign.submit=Eliminar firma
removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Remove Signature
#removeBlanks
@@ -722,8 +706,8 @@ removeAnnotations.submit=Eliminar
#compare
compare.title=Comparar
compare.header=Comparar archivos PDF
compare.highlightColor.1=Color resaltado 1:
compare.highlightColor.2=Color resaltado 2:
compare.highlightColor.1=Highlight Color 1:
compare.highlightColor.2=Highlight Color 2:
compare.document.1=Documento 1
compare.document.2=Documento 2
compare.submit=Comparar
@@ -760,7 +744,7 @@ repair.submit=Reparar
#flatten
flatten.title=Aplanar
flatten.header=Acoplar archivos PDF
flatten.flattenOnlyForms=Aplanar sólo formularios
flatten.flattenOnlyForms=Flatten only forms
flatten.submit=Aplanar
@@ -808,7 +792,7 @@ extractImages.submit=Extraer
fileToPDF.title=Archivo a PDF
fileToPDF.header=Convertir cualquier archivo a PDF
fileToPDF.credit=Este servicio usa LibreOffice y Unoconv para la conversión de archivos
fileToPDF.supportedFileTypesInfo=Tipos de archivos admitidos
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=Los tipos de archivo soportados deben incluir los indicados a continuación; sin embargo, para una completa y acutualizada lista de formatos soportados, por favor consulte la documentación de LibreOffice
fileToPDF.submit=Convertir a PDF
@@ -961,7 +945,6 @@ watermark.selectText.6=Alto (Espacio entre cada marca de agua verticalmente):
watermark.selectText.7=Opacidad (0% - 100%):
watermark.selectText.8=Tipo de marca de agua:
watermark.selectText.9=Imagen de marca de agua:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Añadir marca de agua
watermark.type.1=Texto
watermark.type.2=Imagen
@@ -1017,8 +1000,8 @@ pdfToPDFA.header=PDF a PDF/A
pdfToPDFA.credit=Este servicio usa OCRmyPDF para la conversión a PDF/A
pdfToPDFA.submit=Convertir
pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez
pdfToPDFA.outputFormat=Formato de salida
pdfToPDFA.pdfWithDigitalSignature=El PDF contiene una firma digital. Esto se eliminará en el siguiente paso.
pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord
@@ -1120,13 +1103,13 @@ licenses.version=Versión
licenses.license=Licencia
#survey
survey.nav=Encuesta
survey.title=Encuesta Stirling-PDF
survey.description=Stirling-PDF no tiene seguimiento, por lo que queremos escuchar a nuestros usuarios para mejorar Stirling-PDF.
survey.please=¡Considere realizar nuestra encuesta!
survey.disabled=(La ventana emergente de la encuesta se desactivará en las siguientes actualizaciones, pero estará disponible al pie de la página.)
survey.button=Realizar encuesta
survey.dontShowAgain=No volver a mostrar
survey.nav=Survey
survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
survey.please=Please consider taking our survey!
survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Take Survey
survey.dontShowAgain=Don't show again
#error
@@ -1142,9 +1125,3 @@ error.copyStack=Mostrar seguimiento de pila
error.githubSubmit=GitHub - Enviar un ticket
error.discordSubmit=Discord - Enviar mensaje de soporte
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Ezin da uneko erabiltzailearen rola jaitsi
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Ezin da uneko erabiltzailearen rola jaitsi. Beraz, oraingo erabiltzailea ez da erakutsiko.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
@@ -179,7 +177,6 @@ adminUserSettings.user=Erabiltzaile
adminUserSettings.addUser=Erabiltzaile berria
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Rolak
adminUserSettings.role=Rol
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Gorde Erabiltzailea
adminUserSettings.changeUserRole=Erabiltzailearen rola aldatu
adminUserSettings.authenticated=Authenticated
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Zure kontua blokeatu egin da.
login.signinTitle=Mesedez, hasi saioa
login.ssoSignIn=Hasi saioa Saioa hasteko modu bakarraren bidez
login.oauth2AutoCreateDisabled=OAUTH2 Sortu automatikoki erabiltzailea desgaituta dago
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=Altuera (ur-marka bakoitzaren arteko espazioa bertikalean
watermark.selectText.7=Opakutasuna (0% - 100%):
watermark.selectText.8=Watermark Type:
watermark.selectText.9=Watermark Image:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Gehitu ur-marka
watermark.type.1=Text
watermark.type.2=Image
@@ -1142,9 +1125,3 @@ error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=Utilisateur non trouvé.
incorrectPasswordMessage=Le mot de passe actuel est incorrect.
usernameExistsMessage=Le nouveau nom dutilisateur existe déjà.
invalidUsernameMessage=Nom dutilisateur invalide, le nom dutilisateur ne peut contenir que des lettres, des chiffres et les caractères spéciaux suivants @._+- ou doit être une adresse e-mail valide.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Impossible de supprimer lutilisateur actuellement connecté.
deleteUsernameExistsMessage=Le nom dutilisateur nexiste pas et ne peut pas être supprimé.
downgradeCurrentUserMessage=Impossible de rétrograder le rôle de l'utilisateur actuel.
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Impossible de rétrograder le rôle de l'utilisateur actuel. Par conséquent, l'utilisateur actuel ne sera pas affiché.
userAlreadyExistsOAuthMessage=L'utilisateur existe déjà en tant qu'utilisateur OAuth2.
userAlreadyExistsWebMessage=L'utilisateur existe déjà en tant qu'utilisateur Web.
@@ -179,7 +177,6 @@ adminUserSettings.user=Utilisateur
adminUserSettings.addUser=Ajouter un utilisateur
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Le nom d'utilisateur ne peut contenir que des lettres, des chiffres et les caractères spéciaux suivants @._+- ou doit être une adresse e-mail valide.
adminUserSettings.roles=Rôles
adminUserSettings.role=Rôle
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Forcer lutilisateur à changer son nom dutil
adminUserSettings.submit=Ajouter
adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur
adminUserSettings.authenticated=Authentifié
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=eBook vers PDF
home.BookToPDF.desc=Convertit les formats de livres/bandes dessinées en PDF à l'aide de calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Votre compte a été verrouillé.
login.signinTitle=Veuillez vous connecter
login.ssoSignIn=Se connecter via l'authentification unique
login.oauth2AutoCreateDisabled=OAUTH2 Création automatique d'utilisateur désactivée
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Demande d'autorisation introuvable
login.oauth2InvalidUserInfoResponse=Réponse contenant les informations de l'utilisateur est invalide
login.oauth2invalidRequest=Requête invalide
login.oauth2AccessDenied=Accès refusé
login.oauth2InvalidTokenResponse=Réponse contenant le jeton est invalide
login.oauth2InvalidIdToken=Jeton d'identification invalide
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=heightSpacer (espace entre chaque filigrane verticalement
watermark.selectText.7=Opacité (de 0% à 100%)
watermark.selectText.8=Type de filigrane
watermark.selectText.9=Image du filigrane
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Ajouter un filigrane
watermark.type.1=Text
watermark.type.2=Image
@@ -1142,9 +1125,3 @@ error.copyStack=Copier la Stack Trace
error.githubSubmit=GitHub - Créer un ticket
error.discordSubmit=Discord - Poster un message de demande dassistance
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

File diff suppressed because it is too large Load Diff

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=उपयोगकर्ता नहीं मिला।
incorrectPasswordMessage=वर्तमान पासवर्ड गलत है।
usernameExistsMessage=नया उपयोगकर्ता नाम पहले से मौजूद है।
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता। इसलिए, वर्तमान उपयोगकर्ता को नहीं दिखाया जाएगा।
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
@@ -179,7 +177,6 @@ adminUserSettings.user=उपयोगकर्ता
adminUserSettings.addUser=नया उपयोगकर्ता जोड़ें
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=रोल्स
adminUserSettings.role=रोल
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=उपयोगकर्ता को लॉगि
adminUserSettings.submit=उपयोगकर्ता को सहेजें
adminUserSettings.changeUserRole=यूज़र की भूमिका बदलें
adminUserSettings.authenticated=Authenticated
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=आपका खाता लॉक कर दिया गया
login.signinTitle=कृपया साइन इन करें
login.ssoSignIn=सिंगल साइन - ऑन के ज़रिए लॉग इन करें
login.oauth2AutoCreateDisabled=OAUTH2 ऑटो - क्रिएट यूज़र अक्षम किया गया
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=ऊंचाई स्पेसर (प्रत्ये
watermark.selectText.7=अपारदर्शिता (0% - 100%):
watermark.selectText.8=वॉटरमार्क प्रकार:
watermark.selectText.9=वॉटरमार्क छवि:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=वॉटरमार्क जोड़ें
watermark.type.1=Text
watermark.type.2=Image
@@ -1142,9 +1125,3 @@ error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=Korisnik nije pronađen.
incorrectPasswordMessage=Kriva zaporka.
usernameExistsMessage=Korisničko ime već postoji
invalidUsernameMessage=Nevažeće korisničko ime, korisničko ime može sadržavati samo slova, brojke i sljedeće posebne znakove @._+- ili mora biti važeća adresa e-pošte.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Nije moguće izbrisati trenutno prijavljenog korisnika.
deleteUsernameExistsMessage=Korisničko ime ne postoji i ne može se izbrisati.
downgradeCurrentUserMessage=Nije moguće vratiti unazad ulogu trenutnog korisnika
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Nije moguće vratiti unazad ulogu trenutnog korisnika. Dakle, trenutni korisnik neće biti prikazan.
userAlreadyExistsOAuthMessage=Korisnik već postoji kao OAuth2 korisnik.
userAlreadyExistsWebMessage=Korisnik već postoji kao web korisnik.
@@ -179,7 +177,6 @@ adminUserSettings.user=Korisnik
adminUserSettings.addUser=Dodaj novog korisnika
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Korisničko ime može sadržavati samo slova, brojke i sljedeće posebne znakove @._+- ili mora biti važeća adresa e-pošte.
adminUserSettings.roles=Uloge
adminUserSettings.role=Uloga
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Prisiliti korisnika da promijeni lozinku prilikom
adminUserSettings.submit=Spremi korisnika
adminUserSettings.changeUserRole=Promijenite korisničku ulogu
adminUserSettings.authenticated=Autentificirano
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Book u PDF
home.BookToPDF.desc=Pretvara format knjige/stripa u PDF format pomoću calibre
BookToPDF.tags=Knjiga,Strip,Calibre,Pretvori,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Vaš račun je zaključan.
login.signinTitle=Molimo vas da se prijavite
login.ssoSignIn=Prijavite se putem jedinstvene prijave
login.oauth2AutoCreateDisabled=OAUTH2 automatsko kreiranje korisnika je onemogućeno
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Zahtjev za autorizaciju nije pronađen
login.oauth2InvalidUserInfoResponse=Nevažeće informacije o korisniku
login.oauth2invalidRequest=Neispravan zahtjev
login.oauth2AccessDenied=Pristup odbijen
login.oauth2InvalidTokenResponse=Nevažeći odgovor tokena
login.oauth2InvalidIdToken=Nevažeći ID token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=Visina razmaka (Razmak između svakog vodenog žiga okomi
watermark.selectText.7=Neprozirnost (0% - 100%):
watermark.selectText.8=Vrsta vodenog žiga:
watermark.selectText.9=Slika vodenog žiga:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Dodaj vodeni žig
watermark.type.1=Tekst
watermark.type.2=Slika
@@ -1142,9 +1125,3 @@ error.copyStack=Kopiraj Stack Trace
error.githubSubmit=GitHub - Pošaljite ticket
error.discordSubmit=Discord - Pošalji objavu podrške
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=A felhasználó nem található.
incorrectPasswordMessage=A jelenlegi jelszó helytelen.
usernameExistsMessage=Az új felhasználónév már létezik.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=A jelenlegi felhasználó szerepkörét nem lehet visszaminősíteni
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Az aktuális felhasználó szerepkörét nem lehet visszaminősíteni. Ezért az aktuális felhasználó nem jelenik meg.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
@@ -179,7 +177,6 @@ adminUserSettings.user=Felhasználó
adminUserSettings.addUser=Új felhasználó hozzáadása
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Szerepek
adminUserSettings.role=Szerep
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Kényszerítse a felhasználót a felhasználóné
adminUserSettings.submit=Felhasználó mentése
adminUserSettings.changeUserRole=Felhasználó szerepkörének módosítása
adminUserSettings.authenticated=Authenticated
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=A fiókja zárolva lett!
login.signinTitle=Kérjük, jelentkezzen be!
login.ssoSignIn=Bejelentkezés egyszeri bejelentkezéssel
login.oauth2AutoCreateDisabled=OAUTH2 Felhasználó automatikus létrehozása letiltva
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=heightSpacer (Hely a vízjelek között függőlegesen):
watermark.selectText.7=Átlátszóság (0% - 100%):
watermark.selectText.8=Vízjel típusa:
watermark.selectText.9=Vízjel képe:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Vízjel hozzáadása
watermark.type.1=Text
watermark.type.2=Image
@@ -1142,9 +1125,3 @@ error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=Pengguna tidak ditemukan.
incorrectPasswordMessage=Kata sandi saat ini salah.
usernameExistsMessage=Nama pengguna baru sudah ada.
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Tidak dapat menurunkan peran pengguna saat ini
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Tidak dapat menurunkan peran pengguna saat ini. Oleh karena itu, pengguna saat ini tidak akan ditampilkan.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
@@ -179,7 +177,6 @@ adminUserSettings.user=Pengguna
adminUserSettings.addUser=Tambahkan Pengguna Baru
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Peran
adminUserSettings.role=Peran
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata
adminUserSettings.submit=Simpan Pengguna
adminUserSettings.changeUserRole=Ubah Peran Pengguna
adminUserSettings.authenticated=Authenticated
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Book to PDF
home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Akun Anda telah dikunci.
login.signinTitle=Silakan masuk
login.ssoSignIn=Masuk melalui Single Sign - on
login.oauth2AutoCreateDisabled=OAUTH2 Buat Otomatis Pengguna Dinonaktifkan
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=heightSpacer (Spasi diantara setiap watermark vertikal):
watermark.selectText.7=Opacity (0% - 100%):
watermark.selectText.8=Tipe Watermark:
watermark.selectText.9=Gambar Watermark:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Tambahkan Watermark
watermark.type.1=Text
watermark.type.2=Image
@@ -1142,9 +1125,3 @@ error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=Utente non trovato.
incorrectPasswordMessage=La password attuale non è corretta.
usernameExistsMessage=Il nuovo nome utente esiste già.
invalidUsernameMessage=Nome utente non valido, il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- o deve essere un indirizzo email valido.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=La nuova password e la conferma della nuova password devono corrispondere.
deleteCurrentUserMessage=Impossibile eliminare l'utente attualmente connesso.
deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato.
downgradeCurrentUserMessage=Impossibile declassare il ruolo dell'utente corrente
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Impossibile declassare il ruolo dell'utente corrente. Pertanto, l'utente corrente non verrà visualizzato.
userAlreadyExistsOAuthMessage=L'utente esiste già come utente OAuth2.
userAlreadyExistsWebMessage=L'utente esiste già come utente web.
@@ -179,7 +177,6 @@ adminUserSettings.user=Utente
adminUserSettings.addUser=Aggiungi un nuovo Utente
adminUserSettings.deleteUser=Elimina utente
adminUserSettings.confirmDeleteUser=L'utente deve essere eliminato?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- oppure deve essere un indirizzo email valido.
adminUserSettings.roles=Ruoli
adminUserSettings.role=Ruolo
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password a
adminUserSettings.submit=Salva utente
adminUserSettings.changeUserRole=Cambia il ruolo dell'utente
adminUserSettings.authenticated=Autenticato
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Importazione/Esportazione database
@@ -471,10 +461,6 @@ home.BookToPDF.title=Libro in PDF
home.BookToPDF.desc=Converte i formati di libri/fumetti in PDF utilizzando Calibre
BookToPDF.tags=Libro,fumetto,calibre,conversione,manga,amazon,kindle
home.removeImagePdf.title=Rimuovi immagine
home.removeImagePdf.desc=Rimuovi le immagini dal PDF per ridurre la dimensione del file
removeImagePdf.tags=Rimuovi immagine,operazioni sulla pagina,back-end,lato server
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Il tuo account è stato bloccato.
login.signinTitle=Per favore accedi
login.ssoSignIn=Accedi tramite Single Sign-on
login.oauth2AutoCreateDisabled=Creazione automatica utente OAUTH2 DISABILITATA
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Richiesta di autorizzazione non trovata
login.oauth2InvalidUserInfoResponse=Risposta relativa alle informazioni utente non valida
login.oauth2invalidRequest=Richiesta non valida
login.oauth2AccessDenied=Accesso negato
login.oauth2InvalidTokenResponse=Risposta token non valida
login.oauth2InvalidIdToken=Id Token non valido
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -722,8 +706,8 @@ removeAnnotations.submit=Rimuovi
#compare
compare.title=Compara
compare.header=Compara PDF
compare.highlightColor.1=Evidenzia colore 1:
compare.highlightColor.2=Evidenzia colore 2:
compare.highlightColor.1=Highlight Color 1:
compare.highlightColor.2=Highlight Color 2:
compare.document.1=Documento 1
compare.document.2=Documento 2
compare.submit=Compara
@@ -961,7 +945,6 @@ watermark.selectText.6=spazio verticale (tra ogni filigrana):
watermark.selectText.7=Opacità (0% - 100%):
watermark.selectText.8=Tipo di filigrana:
watermark.selectText.9=Immagine filigrana:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Aggiungi Filigrana
watermark.type.1=Testo
watermark.type.2=Immagine
@@ -1142,9 +1125,3 @@ error.copyStack=Copia traccia dello stack
error.githubSubmit=GitHub: invia un ticket
error.discordSubmit=Discord: invia post di supporto
#remove-image
removeImage.title=Rimuovere immagine
removeImage.header=Rimuovi immagine
removeImage.removeImage=Rimuovi immagine
removeImage.submit=Rimuovi immagine

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=ユーザーが見つかりません。
incorrectPasswordMessage=現在のパスワードが正しくありません。
usernameExistsMessage=新しいユーザー名はすでに存在します。
invalidUsernameMessage=ユーザー名が無効です。ユーザー名には文字、数字、およびそれに続く特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=新しいパスワードと新しいパスワードの確認は一致する必要があります。
deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。
deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。
downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。
userAlreadyExistsOAuthMessage=ユーザーは既にOAuth2ユーザーとして存在します。
userAlreadyExistsWebMessage=ユーザーは既にWebユーザーとして存在します。
@@ -109,7 +107,7 @@ pipelineOptions.validateButton=検証
#############
# NAVBAR #
#############
navbar.favorite=お気に入り
navbar.favorite=Favorites
navbar.darkmode=ダークモード
navbar.language=言語
navbar.settings=設定
@@ -179,7 +177,6 @@ adminUserSettings.user=ユーザー
adminUserSettings.addUser=新しいユーザを追加
adminUserSettings.deleteUser=ユーザの削除
adminUserSettings.confirmDeleteUser=ユーザを本当に削除しますか?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=ユーザー名には、文字、数字、および次の特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
adminUserSettings.roles=役割
adminUserSettings.role=役割
@@ -193,30 +190,23 @@ adminUserSettings.forceChange=ログイン時にユーザー名/パスワード
adminUserSettings.submit=ユーザーの保存
adminUserSettings.changeUserRole=ユーザーの役割を変更する
adminUserSettings.authenticated=認証済
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=データベースのインポート/エクスポート
database.header=データベースのインポート/エクスポート
database.fileName=ファイル名
database.creationDate=作成日
database.fileSize=ファイルサイズ
database.deleteBackupFile=バックアップファイルの削除
database.importBackupFile=バックアップファイルをインポート
database.downloadBackupFile=バックアップファイルをダウンロード
database.info_1=データをインポートする際には、正しい構造を確保することが極めて重要です。不明な点がある場合は、専門家のアドバイスやサポートを受けてください。構造上のエラーは、アプリケーションの誤動作を引き起こす可能性があります。
database.info_2=ファイル名はアップロード時には関係ありません。アップロード後にbackup_user_yyyyMMddHHmm.sqlという形式にリネームされ、一貫した命名規則が保証されます。
database.submit=バックアップをインポート
database.importIntoDatabaseSuccessed=データベースへのインポートに成功
database.fileNotFound=ファイルが見つかりません
database.fileNullOrEmpty=ファイルは null または空であってはなりません
database.failedImportFile=ファイルのインポートに失敗
database.title=Database Import/Export
database.header=Database Import/Export
database.fileName=File Name
database.creationDate=Creation Date
database.fileSize=File Size
database.deleteBackupFile=Delete Backup File
database.importBackupFile=Import Backup File
database.downloadBackupFile=Download Backup File
database.info_1=When importing data, it is crucial to ensure the correct structure. If you are unsure of what you are doing, seek advice and support from a professional. An error in the structure can cause application malfunctions, up to and including the complete inability to run the application.
database.info_2=The file name does not matter when uploading. It will be renamed afterward to follow the format backup_user_yyyyMMddHHmm.sql, ensuring a consistent naming convention.
database.submit=Import Backup
database.importIntoDatabaseSuccessed=Import into database successed
database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
#############
# HOME-PAGE #
@@ -471,10 +461,6 @@ home.BookToPDF.title=PDFを書籍に変換
home.BookToPDF.desc=calibreを使用してPDFを書籍/コミック形式に変換します
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=画像の削除
home.removeImagePdf.desc=PDFから画像を削除してファイルサイズを小さくします
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=あなたのアカウントはロックされています。
login.signinTitle=サインインしてください
login.ssoSignIn=シングルサインオンでログイン
login.oauth2AutoCreateDisabled=OAuth 2自動作成ユーザーが無効
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=認証リクエストが見つかりません
login.oauth2InvalidUserInfoResponse=無効なユーザー情報の応答
login.oauth2invalidRequest=無効なリクエスト
login.oauth2AccessDenied=アクセス拒否
login.oauth2InvalidTokenResponse=無効なトークン応答
login.oauth2InvalidIdToken=無効なIDトークン
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=高さスペース (各透かし間の垂直方向のス
watermark.selectText.7=不透明度 (0% - 100%):
watermark.selectText.8=透かしの種類:
watermark.selectText.9=透かしの画像:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=透かしを追加
watermark.type.1=テキスト
watermark.type.2=画像
@@ -1142,9 +1125,3 @@ error.copyStack=スタックトレースをコピー
error.githubSubmit=GitHub - チケットを提出
error.discordSubmit=Discord - サポート投稿を提出
#remove-image
removeImage.title=画像の削除
removeImage.header=画像の削除
removeImage.removeImage=画像の削除
removeImage.submit=画像を削除

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=사용자를 찾을 수 없습니다.
incorrectPasswordMessage=현재 비밀번호가 틀립니다.
usernameExistsMessage=새 사용자명이 이미 존재합니다.
invalidUsernameMessage=잘못된 사용자 이름입니다. 사용자 이름에는 문자, 숫자 및 다음 특수 문자(@._+-)만 포함할 수 있거나 유효한 이메일 주소여야 합니다.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
deleteCurrentUserMessage=현재 로그인한 사용자를 삭제할 수 없습니다.
deleteUsernameExistsMessage=사용자 이름이 존재하지 않으며 삭제할 수 없습니다.
downgradeCurrentUserMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다. 따라서 현재 사용자는 표시되지 않습니다.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
@@ -179,7 +177,6 @@ adminUserSettings.user=사용자
adminUserSettings.addUser=새 사용자 추가
adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=사용자 이름은 문자, 숫자, 특수 문자 @._+-만 포함할 수 있으며 유효한 이메일 주소여야 합니다.
adminUserSettings.roles=역할
adminUserSettings.role=역할
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=다음 로그인 때 사용자명과 비밀번호
adminUserSettings.submit=사용자 저장
adminUserSettings.changeUserRole=사용자의 역할 변경
adminUserSettings.authenticated=Authenticated
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=책을 PDF로
home.BookToPDF.desc=구경을 사용하여 책/만화 형식을 PDF로 변환
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=계정이 잠겼습니다.
login.signinTitle=로그인해 주세요.
login.ssoSignIn=싱글사인온을 통한 로그인
login.oauth2AutoCreateDisabled=OAUTH2 사용자 자동 생성 비활성화됨
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Invalid Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=세로 간격 (각 워터마크 사이의 세로 공간):
watermark.selectText.7=투명도 (0% - 100%):
watermark.selectText.8=워터마크 유형:
watermark.selectText.9=워터마크 이미지:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=워터마크 추가
watermark.type.1=텍스트
watermark.type.2=이미지
@@ -1142,9 +1125,3 @@ error.copyStack=스택 추적 복사
error.githubSubmit=GitHub - 티켓 제출
error.discordSubmit=Discord - 문의 게시
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=Gebruiker niet gevonden.
incorrectPasswordMessage=Huidige wachtwoord is onjuist.
usernameExistsMessage=Nieuwe gebruikersnaam bestaat al.
invalidUsernameMessage=Ongeldige gebruikersnaam, gebruikersnaam kan alleen letters, nummers en de volgende speciale tekens @._+- bevatten of moet een geldig emailadres zijn.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=Nieuw wachtwoord en bevestig wachtwoord moeten overeenkomen.
deleteCurrentUserMessage=Kan niet een momenteel ingelogde gebruiker verwijderen.
deleteUsernameExistsMessage=De gebruikersnaam bestaat niet en kan niet verwijderd worden.
downgradeCurrentUserMessage=Kan de rol van de huidige gebruiker niet downgraden
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Kan de rol van de huidige gebruiker niet downgraden. Huidige gebruiker wordt dus niet weergegeven.
userAlreadyExistsOAuthMessage=De gebruiker bestaat al als een OAuth2 gebruiker.
userAlreadyExistsWebMessage=De gebruiker bestaat al als een web gebruiker.
@@ -179,7 +177,6 @@ adminUserSettings.user=Gebruiker
adminUserSettings.addUser=Voeg nieuwe gebruiker toe
adminUserSettings.deleteUser=Verwijder gebruiker
adminUserSettings.confirmDeleteUser=Moet deze gebruiker verwijderd worden?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Gebruikersnaam kan alleen letters, nummers en de volgende speciale tekens @._+- bevatten of moet een geldig emailadres zijn.
adminUserSettings.roles=Rollen
adminUserSettings.role=Rol
@@ -193,13 +190,6 @@ adminUserSettings.forceChange=Forceer gebruiker om gebruikersnaam/wachtwoord te
adminUserSettings.submit=Gebruiker opslaan
adminUserSettings.changeUserRole=De rol van de gebruiker wijzigen
adminUserSettings.authenticated=Geauthenticeerd
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Export
@@ -471,10 +461,6 @@ home.BookToPDF.title=Boek naar PDF
home.BookToPDF.desc=Converteert boek-/stripformaat naar PDF met gebruik van Calibre
BookToPDF.tags=Boek,Strip,Comic,Calibre,Converteren,manga,amazon,kindle
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Je account is geblokkeerd.
login.signinTitle=Gelieve in te loggen
login.ssoSignIn=Inloggen via Single Sign-on
login.oauth2AutoCreateDisabled=OAUTH2 Automatisch aanmaken gebruiker uitgeschakeld
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Autorisatieverzoek niet gevonden
login.oauth2InvalidUserInfoResponse=Ongeldige reactie op gebruikersinfo
login.oauth2invalidRequest=Ongeldig verzoek
login.oauth2AccessDenied=Toegang geweigerd
login.oauth2InvalidTokenResponse=Ongeldige tokenreactie
login.oauth2InvalidIdToken=Ongeldige ID token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -961,7 +945,6 @@ watermark.selectText.6=hoogteSpacer (Ruimte tussen elk watermerk verticaal):
watermark.selectText.7=Transparantie (0% - 100%):
watermark.selectText.8=Type watermerk:
watermark.selectText.9=Watermerk afbeelding:
watermark.selectText.10=Convert PDF to PDF-Image
watermark.submit=Watermerk toevoegen
watermark.type.1=Text
watermark.type.2=Image
@@ -1142,9 +1125,3 @@ error.copyStack=Kopieer tracering
error.githubSubmit=GitHub - Dien een ticket in
error.discordSubmit=Discord - Maak een support post
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

View File

@@ -55,12 +55,10 @@ userNotFoundMessage=Bruker ikke funnet.
incorrectPasswordMessage=Nåværende passord er feil.
usernameExistsMessage=Det nye brukernavnet eksisterer allerede.
invalidUsernameMessage=Ugyldig brukernavn, brukernavnet kan bare inneholde bokstaver, tall og følgende spesialtegn @._+- eller må være en gyldig e-postadresse.
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
confirmPasswordErrorMessage=Nytt passord og Bekreft nytt passord må være like.
deleteCurrentUserMessage=Kan ikke slette den innloggede brukeren.
deleteUsernameExistsMessage=Brukernavnet eksisterer ikke og kan ikke slettes.
downgradeCurrentUserMessage=Kan ikke nedgradere den innloggede brukerens rolle.
disabledCurrentUserMessage=The current user cannot be disabled
downgradeCurrentUserLongMessage=Kan ikke nedgradere den innloggede brukerens rolle. Derfor vil ikke den innloggede brukeren bli vist.
userAlreadyExistsOAuthMessage=Brukeren eksisterer allerede som en OAuth2-bruker.
userAlreadyExistsWebMessage=Brukeren eksisterer allerede som en web-bruker.
@@ -179,7 +177,6 @@ adminUserSettings.user=Bruker
adminUserSettings.addUser=Legg til Ny Bruker
adminUserSettings.deleteUser=Slett Bruker
adminUserSettings.confirmDeleteUser=Skal brukeren slettes?
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
adminUserSettings.usernameInfo=Brukernavn kan bare inneholde bokstaver, tall og følgende spesialtegn @._+- eller må være en gyldig e-postadresse.
adminUserSettings.roles=Roller
adminUserSettings.role=Rolle
@@ -193,30 +190,23 @@ adminUserSettings.forceChange=Tving bruker til å endre passord ved innlogging
adminUserSettings.submit=Lagre Bruker
adminUserSettings.changeUserRole=Endre Brukerens Rolle
adminUserSettings.authenticated=Autentisert
adminUserSettings.editOwnProfil=Edit own profile
adminUserSettings.enabledUser=enabled user
adminUserSettings.disabledUser=disabled user
adminUserSettings.activeUsers=Active Users:
adminUserSettings.disabledUsers=Disabled Users:
adminUserSettings.totalUsers=Total Users:
adminUserSettings.lastRequest=Last Request
database.title=Database Import/Eksport
database.header=Database Import/Eksport
database.fileName=Fil navn
database.creationDate=Opprettelsesdato
database.fileSize=Filstørrelse
database.deleteBackupFile=Slett sikkerhetskopifil
database.importBackupFile=Importer sikkerhetskopifil
database.downloadBackupFile=Last ned sikkerhetskopifil
database.info_1=Når du importerer data, er det avgjørende å sikre riktig struktur. Hvis du er usikker på hva du gjør, bør du søke råd og støtte fra en profesjonell. En feil i strukturen kan føre til applikasjonsfeil, inkludert fullstendig manglende evne til å kjøre applikasjonen.
database.info_2=Filnavnet spiller ingen rolle ved opplasting. Det vil bli omdøpt etterpå for å følge formatet backup_user_yyyyMMddHHmm.sql, for å sikre en konsekvent navnekonvensjon.
database.submit=Importer sikkerhetskopi
database.importIntoDatabaseSuccessed=Import til database vellykket
database.fileNotFound=Fil ikke funnet
database.fileNullOrEmpty=Fil må ikke være tom eller null
database.failedImportFile=Import av fil mislyktes
database.title=Database Import/Export
database.header=Database Import/Export
database.fileName=File Name
database.creationDate=Creation Date
database.fileSize=File Size
database.deleteBackupFile=Delete Backup File
database.importBackupFile=Import Backup File
database.downloadBackupFile=Download Backup File
database.info_1=When importing data, it is crucial to ensure the correct structure. If you are unsure of what you are doing, seek advice and support from a professional. An error in the structure can cause application malfunctions, up to and including the complete inability to run the application.
database.info_2=The file name does not matter when uploading. It will be renamed afterward to follow the format backup_user_yyyyMMddHHmm.sql, ensuring a consistent naming convention.
database.submit=Import Backup
database.importIntoDatabaseSuccessed=Import into database successed
database.fileNotFound=File not Found
database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Failed Import File
#############
# HOME-PAGE #
@@ -471,10 +461,6 @@ home.BookToPDF.title=Bok til PDF
home.BookToPDF.desc=Konverter bøker/tegneserier til PDF ved hjelp av calibre
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf
home.removeImagePdf.title=Remove image
home.removeImagePdf.desc=Remove image from PDF to reduce file size
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
###########################
# #
@@ -491,14 +477,12 @@ login.locked=Kontoen din har blitt låst.
login.signinTitle=Vennligst logg inn
login.ssoSignIn=Logg inn via Enkel Pålogging
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Opretting av bruker deaktivert
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
login.oauth2RequestNotFound=Autentiseringsforespørsel ikke funnet
login.oauth2InvalidUserInfoResponse=Ugyldig brukerinforespons
login.oauth2invalidRequest=Ugyldig forespørsel
login.oauth2AccessDenied=Tilgang nektet
login.oauth2InvalidTokenResponse=Ugyldig tokenrespons
login.oauth2InvalidIdToken=Ugyldig Id Token
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
#auto-redact
@@ -722,8 +706,8 @@ removeAnnotations.submit=Fjern
#compare
compare.title=Sammenlign
compare.header=Sammenlign PDF-er
compare.highlightColor.1=Uthevingsfarge 1:
compare.highlightColor.2=Uthevingsfarge 2:
compare.highlightColor.1=Highlight Color 1:
compare.highlightColor.2=Highlight Color 2:
compare.document.1=Dokument 1
compare.document.2=Dokument 2
compare.submit=Sammenlign
@@ -961,7 +945,6 @@ watermark.selectText.6=Høydeavstand (Avstand mellom hvert vannmerke vertikalt):
watermark.selectText.7=Opasitet (0% - 100%):
watermark.selectText.8=Vannmerketype:
watermark.selectText.9=Vannmerkebilde:
watermark.selectText.10=Konverter PDF til PDF-Bilde
watermark.submit=Legg til vannmerke
watermark.type.1=Tekst
watermark.type.2=Bilde
@@ -1142,9 +1125,3 @@ error.copyStack=Kopier stakksporing
error.githubSubmit=GitHub - Send inn en billett
error.discordSubmit=Discord - Send inn støtteinnlegg
#remove-image
removeImage.title=Remove image
removeImage.header=Remove image
removeImage.removeImage=Remove image
removeImage.submit=Remove image

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