Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d75e4f1318 | ||
|
|
4088132597 | ||
|
|
7b9d419267 | ||
|
|
9b710b489e | ||
|
|
e2ff418c89 | ||
|
|
be006f08a6 | ||
|
|
b007c805bf | ||
|
|
24be10eed4 | ||
|
|
0854a1d26e | ||
|
|
33c7bb7e13 | ||
|
|
cdf31622e2 | ||
|
|
c7e5987342 | ||
|
|
d3ef335c24 | ||
|
|
b23784f598 | ||
|
|
90cbcde029 | ||
|
|
382edc01f8 | ||
|
|
dee912f075 | ||
|
|
faed367cde | ||
|
|
788744c1be | ||
|
|
686b88d21d | ||
|
|
1a594b27ab | ||
|
|
9f0088c839 | ||
|
|
5564a6e730 | ||
|
|
8a58647ffd | ||
|
|
37dcae282a | ||
|
|
58618b3a21 | ||
|
|
4a4c7faf47 | ||
|
|
66324a5bdc | ||
|
|
3bd18f7c5e | ||
|
|
e693bbb2bd | ||
|
|
f11ad92fa5 | ||
|
|
f443a4e0de | ||
|
|
fa0152aa2d | ||
|
|
e1d0f2cd3e | ||
|
|
81e2a77e57 | ||
|
|
6c9a4e8acc | ||
|
|
8e5b3ea7f1 | ||
|
|
b47f8a2c17 | ||
|
|
56a07bbf3a | ||
|
|
987d793ef4 | ||
|
|
ea85d76f5b | ||
|
|
fc762329a8 | ||
|
|
48bae227f6 | ||
|
|
9773138612 | ||
|
|
1927801894 | ||
|
|
3cd6f462a8 | ||
|
|
6dab3980fc | ||
|
|
ea2d755808 | ||
|
|
05efcedea8 | ||
|
|
29fcbf30d7 | ||
|
|
2cbe34ea24 | ||
|
|
29f43c010e | ||
|
|
8602f38fbf | ||
|
|
f5258c593b | ||
|
|
e89ac84928 | ||
|
|
8997855922 | ||
|
|
09c93cebe3 | ||
|
|
00d65596d1 | ||
|
|
851b43fadf | ||
|
|
d5ac560452 | ||
|
|
4ea323b879 | ||
|
|
5c84ae1b53 | ||
|
|
96a8898c15 | ||
|
|
1cca13334e | ||
|
|
264763dd4c | ||
|
|
eafbfb8dbf | ||
|
|
6fa7c2e5e1 | ||
|
|
909054a49d | ||
|
|
711501a382 | ||
|
|
e45b512087 | ||
|
|
d32da95f55 | ||
|
|
b54d73d723 | ||
|
|
503a1c9526 | ||
|
|
f176558a39 | ||
|
|
68c387086c | ||
|
|
f165439d26 | ||
|
|
6649ffd7a0 | ||
|
|
8dbbacb09e |
14
.github/ISSUE_TEMPLATE/1-bug.yml
vendored
14
.github/ISSUE_TEMPLATE/1-bug.yml
vendored
@@ -10,7 +10,19 @@ body:
|
|||||||
Thanks for taking the time to fill out this bug report!
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
|
||||||
This issue form is for reporting bugs only. Please fill out the following sections to help us understand the issue you are facing.
|
This issue form is for reporting bugs only. Please fill out the following sections to help us understand the issue you are facing.
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: installation-method
|
||||||
|
attributes:
|
||||||
|
label: Installation Method
|
||||||
|
description: |
|
||||||
|
Indicate whether you are using Docker or a local installation.
|
||||||
|
options:
|
||||||
|
- Docker
|
||||||
|
- Docker ultra lite
|
||||||
|
- Docker fat
|
||||||
|
- Local Installation
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: problem
|
id: problem
|
||||||
validations:
|
validations:
|
||||||
|
|||||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -9,6 +9,8 @@ updates:
|
|||||||
directory: "/" # Location of package manifests
|
directory: "/" # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
rebase-strategy: "auto"
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directory: "/" # Location of Dockerfile
|
directory: "/" # Location of Dockerfile
|
||||||
schedule:
|
schedule:
|
||||||
|
|||||||
5
.github/labeler-config.yml
vendored
5
.github/labeler-config.yml
vendored
@@ -2,6 +2,7 @@ Translation:
|
|||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'src/main/resources/messages_*_*.properties'
|
- any-glob-to-any-file: 'src/main/resources/messages_*_*.properties'
|
||||||
- any-glob-to-any-file: 'scripts/ignore_translation.toml'
|
- any-glob-to-any-file: 'scripts/ignore_translation.toml'
|
||||||
|
- any-glob-to-any-file: 'src/main/resources/templates/fragments/languages.html'
|
||||||
|
|
||||||
Front End:
|
Front End:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
@@ -47,3 +48,7 @@ Test:
|
|||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'cucumber/**/*'
|
- any-glob-to-any-file: 'cucumber/**/*'
|
||||||
- any-glob-to-any-file: 'src/test**/*'
|
- any-glob-to-any-file: 'src/test**/*'
|
||||||
|
|
||||||
|
Github:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: '.github/**/*'
|
||||||
|
|||||||
6
.github/pull_request_template.md
vendored
6
.github/pull_request_template.md
vendored
@@ -10,9 +10,3 @@ Closes #(issue_number)
|
|||||||
- [ ] I have performed a self-review of my own code
|
- [ ] I have performed a self-review of my own code
|
||||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||||
- [ ] My changes generate no new warnings
|
- [ ] My changes generate no new warnings
|
||||||
|
|
||||||
## Contributor License Agreement
|
|
||||||
|
|
||||||
By submitting this pull request, I acknowledge and agree that my contributions will be included in Stirling-PDF and that they can be relicensed in the future under the MPL 2.0 (Mozilla Public License Version 2.0) license.
|
|
||||||
|
|
||||||
(This does not change the general open-source nature of Stirling-PDF, simply moving from one license to another license)
|
|
||||||
|
|||||||
32
.github/release.yml
vendored
Normal file
32
.github/release.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
changelog:
|
||||||
|
exclude:
|
||||||
|
labels:
|
||||||
|
- Documentation
|
||||||
|
- Test
|
||||||
|
- Github
|
||||||
|
|
||||||
|
categories:
|
||||||
|
- title: Bug Fixes
|
||||||
|
labels:
|
||||||
|
- Bug
|
||||||
|
|
||||||
|
- title: Enhancements
|
||||||
|
labels:
|
||||||
|
- enhancement
|
||||||
|
|
||||||
|
- title: Minor Enhancements
|
||||||
|
labels:
|
||||||
|
- Java
|
||||||
|
- Front End
|
||||||
|
|
||||||
|
- title: Docker Updates
|
||||||
|
labels:
|
||||||
|
- Docker
|
||||||
|
|
||||||
|
- title: Translation Changes
|
||||||
|
labels:
|
||||||
|
- Translation
|
||||||
|
|
||||||
|
- title: Other Changes
|
||||||
|
labels:
|
||||||
|
- "*"
|
||||||
4
.github/workflows/auto-labeler.yml
vendored
4
.github/workflows/auto-labeler.yml
vendored
@@ -11,7 +11,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/labeler@v5
|
|
||||||
|
- name: Apply Labels
|
||||||
|
uses: actions/labeler@v5
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
configuration-path: .github/labeler-config.yml
|
configuration-path: .github/labeler-config.yml
|
||||||
|
|||||||
60
.github/workflows/build.yml
vendored
60
.github/workflows/build.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: "Build repo"
|
name: Build repo
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -17,20 +17,72 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
jdk-version: [17, 21]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK ${{ matrix.jdk-version }}
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: "17"
|
java-version: ${{ matrix.jdk-version }}
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v3
|
- name: Set up Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v4
|
||||||
with:
|
with:
|
||||||
gradle-version: 8.7
|
gradle-version: 8.7
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew build --no-build-cache
|
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@v5
|
||||||
|
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
|
||||||
|
|||||||
19
.github/workflows/licenses-update.yml
vendored
19
.github/workflows/licenses-update.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
java-version: "17"
|
java-version: "17"
|
||||||
distribution: "adopt"
|
distribution: "adopt"
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v3
|
- uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
- name: Run Gradle Command
|
- name: Run Gradle Command
|
||||||
run: ./gradlew clean generateLicenseReport
|
run: ./gradlew clean generateLicenseReport
|
||||||
@@ -45,6 +45,7 @@ jobs:
|
|||||||
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
|
git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
|
id: cpr
|
||||||
if: env.CHANGES_DETECTED == 'true'
|
if: env.CHANGES_DETECTED == 'true'
|
||||||
uses: peter-evans/create-pull-request@v6
|
uses: peter-evans/create-pull-request@v6
|
||||||
with:
|
with:
|
||||||
@@ -57,6 +58,22 @@ jobs:
|
|||||||
title: "Update 3rd Party Licenses"
|
title: "Update 3rd Party Licenses"
|
||||||
body: |
|
body: |
|
||||||
Auto-generated by [create-pull-request][1]
|
Auto-generated by [create-pull-request][1]
|
||||||
|
|
||||||
[1]: https://github.com/peter-evans/create-pull-request
|
[1]: https://github.com/peter-evans/create-pull-request
|
||||||
|
labels: licenses
|
||||||
draft: false
|
draft: false
|
||||||
delete-branch: true
|
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
|
||||||
|
|||||||
9
.github/workflows/push-docker.yml
vendored
9
.github/workflows/push-docker.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
java-version: "17"
|
java-version: "17"
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v3
|
- uses: gradle/actions/setup-gradle@v4
|
||||||
with:
|
with:
|
||||||
gradle-version: 8.7
|
gradle-version: 8.7
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ jobs:
|
|||||||
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
|
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
|
||||||
|
|
||||||
- name: Build and push main Dockerfile
|
- name: Build and push main Dockerfile
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
@@ -98,7 +98,7 @@ jobs:
|
|||||||
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|
||||||
- name: Build and push Dockerfile-ultra-lite
|
- name: Build and push Dockerfile-ultra-lite
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
if: github.ref != 'refs/heads/main'
|
if: github.ref != 'refs/heads/main'
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
@@ -111,7 +111,6 @@ jobs:
|
|||||||
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
|
||||||
|
|
||||||
- name: Generate tags fat
|
- name: Generate tags fat
|
||||||
id: meta3
|
id: meta3
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
@@ -125,7 +124,7 @@ jobs:
|
|||||||
type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/master' }}
|
type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|
||||||
- name: Build and push main Dockerfile fat
|
- name: Build and push main Dockerfile fat
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
if: github.ref != 'refs/heads/main'
|
if: github.ref != 'refs/heads/main'
|
||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
|
|||||||
2
.github/workflows/releaseArtifacts.yml
vendored
2
.github/workflows/releaseArtifacts.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
|||||||
java-version: "17"
|
java-version: "17"
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v3
|
- uses: gradle/actions/setup-gradle@v4
|
||||||
with:
|
with:
|
||||||
gradle-version: 8.7
|
gradle-version: 8.7
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/swagger.yml
vendored
2
.github/workflows/swagger.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
|||||||
java-version: "17"
|
java-version: "17"
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
|
|
||||||
- uses: gradle/actions/setup-gradle@v3
|
- uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
- name: Generate Swagger documentation
|
- name: Generate Swagger documentation
|
||||||
run: ./gradlew generateOpenApiDocs
|
run: ./gradlew generateOpenApiDocs
|
||||||
|
|||||||
47
.github/workflows/test.yml
vendored
47
.github/workflows/test.yml
vendored
@@ -1,47 +0,0 @@
|
|||||||
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.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
|
|
||||||
@@ -39,16 +39,16 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
|
|||||||
libreoffice \
|
libreoffice \
|
||||||
# pdftohtml
|
# pdftohtml
|
||||||
poppler-utils \
|
poppler-utils \
|
||||||
# OCR MY PDF (unpaper for descew and other advanced featues)
|
# OCR MY PDF (unpaper for descew and other advanced features)
|
||||||
ocrmypdf \
|
ocrmypdf \
|
||||||
tesseract-ocr-data-eng \
|
tesseract-ocr-data-eng \
|
||||||
# CV
|
# CV
|
||||||
py3-opencv \
|
py3-opencv \
|
||||||
# python3/pip
|
# python3/pip
|
||||||
python3 \
|
python3 \
|
||||||
py3-pip && \
|
py3-pip && \
|
||||||
# uno unoconv and HTML
|
# uno unoconv and HTML
|
||||||
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
|
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint pdf2image pillow && \
|
||||||
mv /usr/share/tessdata /usr/share/tessdata-original && \
|
mv /usr/share/tessdata /usr/share/tessdata-original && \
|
||||||
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
|
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
|
||||||
fc-cache -f -v && \
|
fc-cache -f -v && \
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ COPY . .
|
|||||||
|
|
||||||
# Build the application with DOCKER_ENABLE_SECURITY=false
|
# Build the application with DOCKER_ENABLE_SECURITY=false
|
||||||
RUN DOCKER_ENABLE_SECURITY=true \
|
RUN DOCKER_ENABLE_SECURITY=true \
|
||||||
./gradlew clean build
|
./gradlew clean build
|
||||||
|
|
||||||
# Main stage
|
# Main stage
|
||||||
FROM alpine:3.20.2
|
FROM alpine:3.20.2
|
||||||
@@ -32,7 +32,7 @@ ENV DOCKER_ENABLE_SECURITY=false \
|
|||||||
UMASK=022 \
|
UMASK=022 \
|
||||||
FAT_DOCKER=true \
|
FAT_DOCKER=true \
|
||||||
INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
|
INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
|
||||||
|
|
||||||
|
|
||||||
# JDK for app
|
# JDK for app
|
||||||
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
@@ -64,7 +64,7 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et
|
|||||||
python3 \
|
python3 \
|
||||||
py3-pip && \
|
py3-pip && \
|
||||||
# uno unoconv and HTML
|
# uno unoconv and HTML
|
||||||
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \
|
pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint pdf2image pillow && \
|
||||||
mv /usr/share/tessdata /usr/share/tessdata-original && \
|
mv /usr/share/tessdata /usr/share/tessdata-original && \
|
||||||
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
|
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \
|
||||||
fc-cache -f -v && \
|
fc-cache -f -v && \
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
| file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| file-to-pdf | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | |
|
| img-to-pdf | | ✔️ | | | | | | | | ✔️ | |
|
||||||
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-html | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
| pdf-to-img | | ✔️ | | | | | | | | ✔️ | |
|
| pdf-to-img | | ✔️ | | | | ✔️ | | | | ✔️ | |
|
||||||
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | |
|
| pdf-to-pdfa | | ✔️ | | | ✔️ | | | | ✔️ | | |
|
||||||
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | |
|
| pdf-to-markdown | | ✔️ | | | | | | | | ✔️ | |
|
||||||
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
| pdf-to-presentation | | ✔️ | | | ✔️ | | | ✔️ | | | |
|
||||||
@@ -44,4 +44,4 @@
|
|||||||
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
| remove-blanks | | | | ✔️ | ✔️ | ✔️ | ✔️ | | | | |
|
||||||
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | |
|
| repair | | | | ✔️ | ✔️ | | | ✔️ | | | |
|
||||||
| show-javascript | | | | ✔️ | | | | | | | ✔️ |
|
| show-javascript | | | | ✔️ | | | | | | | ✔️ |
|
||||||
| sign | | | | ✔️ | | | | | | | ✔️ |
|
| sign | | | | ✔️ | | | | | | | ✔️ |
|
||||||
|
|||||||
67
README.md
67
README.md
@@ -169,42 +169,42 @@ Stirling PDF currently supports 38!
|
|||||||
|
|
||||||
| Language | Progress |
|
| Language | Progress |
|
||||||
| ------------------------------------------- | -------------------------------------- |
|
| ------------------------------------------- | -------------------------------------- |
|
||||||
| Arabic (العربية) (ar_AR) |  |
|
| Arabic (العربية) (ar_AR) |  |
|
||||||
| Basque (Euskara) (eu_ES) |  |
|
| Basque (Euskara) (eu_ES) |  |
|
||||||
| Bulgarian (Български) (bg_BG) |  |
|
| Bulgarian (Български) (bg_BG) |  |
|
||||||
| Catalan (Català) (ca_CA) |  |
|
| Catalan (Català) (ca_CA) |  |
|
||||||
| Croatian (Hrvatski) (hr_HR) |  |
|
| Croatian (Hrvatski) (hr_HR) |  |
|
||||||
| Czech (Česky) (cs_CZ) |  |
|
| Czech (Česky) (cs_CZ) |  |
|
||||||
| Danish (Dansk) (da_DK) |  |
|
| Danish (Dansk) (da_DK) |  |
|
||||||
| Dutch (Nederlands) (nl_NL) |  |
|
| Dutch (Nederlands) (nl_NL) |  |
|
||||||
| English (English) (en_GB) |  |
|
| English (English) (en_GB) |  |
|
||||||
| English (US) (en_US) |  |
|
| English (US) (en_US) |  |
|
||||||
| French (Français) (fr_FR) |  |
|
| French (Français) (fr_FR) |  |
|
||||||
| German (Deutsch) (de_DE) |  |
|
| German (Deutsch) (de_DE) |  |
|
||||||
| Greek (Ελληνικά) (el_GR) |  |
|
| Greek (Ελληνικά) (el_GR) |  |
|
||||||
| Hindi (हिंदी) (hi_IN) |  |
|
| Hindi (हिंदी) (hi_IN) |  |
|
||||||
| Hungarian (Magyar) (hu_HU) |  |
|
| Hungarian (Magyar) (hu_HU) |  |
|
||||||
| Indonesia (Bahasa Indonesia) (id_ID) |  |
|
| Indonesia (Bahasa Indonesia) (id_ID) |  |
|
||||||
| Irish (Gaeilge) (ga_IE) |  |
|
| Irish (Gaeilge) (ga_IE) |  |
|
||||||
| Italian (Italiano) (it_IT) |  |
|
| Italian (Italiano) (it_IT) |  |
|
||||||
| Japanese (日本語) (ja_JP) |  |
|
| Japanese (日本語) (ja_JP) |  |
|
||||||
| Korean (한국어) (ko_KR) |  |
|
| Korean (한국어) (ko_KR) |  |
|
||||||
| Norwegian (Norsk) (no_NB) |  |
|
| Norwegian (Norsk) (no_NB) |  |
|
||||||
| Polish (Polski) (pl_PL) |  |
|
| Polish (Polski) (pl_PL) |  |
|
||||||
| Portuguese (Português) (pt_PT) |  |
|
| Portuguese (Português) (pt_PT) |  |
|
||||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||||
| Romanian (Română) (ro_RO) |  |
|
| Romanian (Română) (ro_RO) |  |
|
||||||
| Russian (Русский) (ru_RU) |  |
|
| Russian (Русский) (ru_RU) |  |
|
||||||
| Sebian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||||
| Slovakian (Slovensky) (sk_SK) |  |
|
| Slovakian (Slovensky) (sk_SK) |  |
|
||||||
| Spanish (Español) (es_ES) |  |
|
| Spanish (Español) (es_ES) |  |
|
||||||
| Swedish (Svenska) (sv_SE) |  |
|
| Swedish (Svenska) (sv_SE) |  |
|
||||||
| Thai (ไทย) (th_TH) |  |
|
| Thai (ไทย) (th_TH) |  |
|
||||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||||
| Turkish (Türkçe) (tr_TR) |  |
|
| Turkish (Türkçe) (tr_TR) |  |
|
||||||
| Ukrainian (Українська) (uk_UA) |  |
|
| Ukrainian (Українська) (uk_UA) |  |
|
||||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||||
|
|
||||||
## Contributing (creating issues, translations, fixing bugs, etc.)
|
## Contributing (creating issues, translations, fixing bugs, etc.)
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ The Current list of settings is
|
|||||||
security:
|
security:
|
||||||
enableLogin: false # set to 'true' to enable login
|
enableLogin: false # set to 'true' to enable login
|
||||||
csrfDisabled: true # Set to 'true' to disable CSRF protection (not recommended for production)
|
csrfDisabled: true # Set to 'true' to disable CSRF protection (not recommended for production)
|
||||||
loginAttemptCount: 5 # lock user account after 5 tries
|
loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1
|
||||||
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
|
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
|
||||||
loginMethod: all # 'all' (Login Username/Password and OAuth2[must be enabled and configured]), 'normal'(only Login with Username/Password) or 'oauth2'(only Login with OAuth2)
|
loginMethod: all # 'all' (Login Username/Password and OAuth2[must be enabled and configured]), 'normal'(only Login with Username/Password) or 'oauth2'(only Login with OAuth2)
|
||||||
initialLogin:
|
initialLogin:
|
||||||
@@ -266,6 +266,7 @@ security:
|
|||||||
clientId: '' # Client ID from your provider
|
clientId: '' # Client ID from your provider
|
||||||
clientSecret: '' # Client Secret from your provider
|
clientSecret: '' # Client Secret from your provider
|
||||||
autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
|
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
|
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
|
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'
|
provider: google # Set this to your OAuth provider's name, e.g., 'google' or 'keycloak'
|
||||||
|
|||||||
95
build.gradle
95
build.gradle
@@ -4,19 +4,25 @@ plugins {
|
|||||||
id "io.spring.dependency-management" version "1.1.6"
|
id "io.spring.dependency-management" version "1.1.6"
|
||||||
id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
|
id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
|
||||||
id "io.swagger.swaggerhub" version "1.3.2"
|
id "io.swagger.swaggerhub" version "1.3.2"
|
||||||
id "edu.sc.seis.launch4j" version "3.0.5"
|
id "edu.sc.seis.launch4j" version "3.0.6"
|
||||||
id "com.diffplug.spotless" version "6.25.0"
|
id "com.diffplug.spotless" version "6.25.0"
|
||||||
id "com.github.jk1.dependency-license-report" version "2.8"
|
id "com.github.jk1.dependency-license-report" version "2.9"
|
||||||
|
//id "nebula.lint" version "19.0.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
import com.github.jk1.license.render.*
|
import com.github.jk1.license.render.*
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
springBootVersion = "3.3.2"
|
springBootVersion = "3.3.2"
|
||||||
|
pdfboxVersion = "3.0.3"
|
||||||
|
logbackVersion = "1.5.7"
|
||||||
|
imageioVersion = "3.11.0"
|
||||||
|
lombokVersion = "1.18.34"
|
||||||
|
bouncycastleVersion = "1.78.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "stirling.software"
|
group = "stirling.software"
|
||||||
version = "0.27.0"
|
version = "0.28.3"
|
||||||
|
|
||||||
java {
|
java {
|
||||||
// 17 is lowest but we support and recommend 21
|
// 17 is lowest but we support and recommend 21
|
||||||
@@ -45,6 +51,7 @@ sourceSets {
|
|||||||
exclude "stirling/software/SPDF/model/AttemptCounter.java"
|
exclude "stirling/software/SPDF/model/AttemptCounter.java"
|
||||||
exclude "stirling/software/SPDF/model/Authority.java"
|
exclude "stirling/software/SPDF/model/Authority.java"
|
||||||
exclude "stirling/software/SPDF/model/PersistentLogin.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/model/User.java"
|
||||||
exclude "stirling/software/SPDF/repository/**"
|
exclude "stirling/software/SPDF/repository/**"
|
||||||
}
|
}
|
||||||
@@ -94,37 +101,41 @@ spotless {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//gradleLint {
|
||||||
|
// rules=['unused-dependency']
|
||||||
|
// }
|
||||||
tasks.wrapper {
|
tasks.wrapper {
|
||||||
gradleVersion = "8.7"
|
gradleVersion = "8.7"
|
||||||
}
|
}
|
||||||
|
//tasks.withType(JavaCompile) {
|
||||||
|
// options.compilerArgs << "-Xlint:deprecation"
|
||||||
|
//}
|
||||||
|
configurations.all {
|
||||||
|
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
|
||||||
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
//security updates
|
//security updates
|
||||||
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 "org.springframework:spring-webmvc:6.1.9"
|
||||||
|
|
||||||
implementation("io.github.pixee:java-security-toolkit:1.1.3")
|
implementation("io.github.pixee:java-security-toolkit:1.2.0")
|
||||||
|
|
||||||
// implementation "org.yaml:snakeyaml:2.2"
|
// implementation "org.yaml:snakeyaml:2.2"
|
||||||
implementation 'com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4'
|
implementation 'com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4'
|
||||||
|
|
||||||
// Exclude Tomcat and include Jetty
|
// Exclude Tomcat and include Jetty
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion") {
|
implementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
|
||||||
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
|
|
||||||
}
|
|
||||||
implementation "org.springframework.boot:spring-boot-starter-jetty:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-jetty:$springBootVersion"
|
||||||
|
|
||||||
implementation "org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion"
|
||||||
|
|
||||||
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") {
|
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") {
|
||||||
implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
|
||||||
implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE"
|
runtimeOnly "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE"
|
||||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
|
||||||
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
|
||||||
|
|
||||||
//2.2.x requires rebuild of DB file.. need migration path
|
//2.2.x requires rebuild of DB file.. need migration path
|
||||||
implementation "com.h2database:h2:2.1.214"
|
runtimeOnly "com.h2database:h2:2.1.214"
|
||||||
// implementation "com.h2database:h2:2.2.224"
|
// implementation "com.h2database:h2:2.2.224"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,28 +143,27 @@ dependencies {
|
|||||||
|
|
||||||
// Batik
|
// Batik
|
||||||
implementation "org.apache.xmlgraphics:batik-all:1.17"
|
implementation "org.apache.xmlgraphics:batik-all:1.17"
|
||||||
|
|
||||||
// TwelveMonkeys
|
// TwelveMonkeys
|
||||||
implementation "com.twelvemonkeys.imageio:imageio-batik:3.11.0"
|
runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
|
||||||
implementation "com.twelvemonkeys.imageio:imageio-bmp:3.11.0"
|
runtimeOnly "com.twelvemonkeys.imageio:imageio-bmp:$imageioVersion"
|
||||||
// implementation "com.twelvemonkeys.imageio:imageio-hdr:3.10.1"
|
// runtimeOnly "com.twelvemonkeys.imageio:imageio-hdr:$imageioVersion"
|
||||||
// implementation "com.twelvemonkeys.imageio:imageio-icns:3.10.1"
|
// runtimeOnly "com.twelvemonkeys.imageio:imageio-icns:$imageioVersion"
|
||||||
// implementation "com.twelvemonkeys.imageio:imageio-iff:3.10.1"
|
// runtimeOnly "com.twelvemonkeys.imageio:imageio-iff:$imageioVersion"
|
||||||
implementation "com.twelvemonkeys.imageio:imageio-jpeg:3.11.0"
|
runtimeOnly "com.twelvemonkeys.imageio:imageio-jpeg:$imageioVersion"
|
||||||
// implementation "com.twelvemonkeys.imageio:imageio-pcx:3.10.1"
|
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pcx:$imageioVersion@
|
||||||
// implementation "com.twelvemonkeys.imageio:imageio-pict:3.10.1"
|
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pict:$imageioVersion"
|
||||||
// implementation "com.twelvemonkeys.imageio:imageio-pnm:3.10.1"
|
// runtimeOnly "com.twelvemonkeys.imageio:imageio-pnm:$imageioVersion"
|
||||||
// implementation "com.twelvemonkeys.imageio:imageio-psd:3.10.1"
|
// runtimeOnly "com.twelvemonkeys.imageio:imageio-psd:$imageioVersion"
|
||||||
// implementation "com.twelvemonkeys.imageio:imageio-sgi:3.10.1"
|
// runtimeOnly "com.twelvemonkeys.imageio:imageio-sgi:$imageioVersion"
|
||||||
// implementation "com.twelvemonkeys.imageio:imageio-tga:3.10.1"
|
// runtimeOnly "com.twelvemonkeys.imageio:imageio-tga:$imageioVersion"
|
||||||
// implementation "com.twelvemonkeys.imageio:imageio-thumbsdb:3.10.1"
|
// runtimeOnly "com.twelvemonkeys.imageio:imageio-thumbsdb:$imageioVersion"
|
||||||
implementation "com.twelvemonkeys.imageio:imageio-tiff:3.11.0"
|
runtimeOnly "com.twelvemonkeys.imageio:imageio-tiff:$imageioVersion"
|
||||||
implementation "com.twelvemonkeys.imageio:imageio-webp:3.11.0"
|
runtimeOnly "com.twelvemonkeys.imageio:imageio-webp:$imageioVersion"
|
||||||
// implementation "com.twelvemonkeys.imageio:imageio-xwd:3.10.1"
|
// runtimeOnly "com.twelvemonkeys.imageio:imageio-xwd:$imageioVersion"
|
||||||
|
|
||||||
implementation "commons-io:commons-io:2.16.1"
|
implementation "commons-io:commons-io:2.16.1"
|
||||||
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0"
|
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0"
|
||||||
|
|
||||||
//general PDF
|
//general PDF
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/com.opencsv/opencsv
|
// https://mvnrepository.com/artifact/com.opencsv/opencsv
|
||||||
@@ -161,33 +171,36 @@ dependencies {
|
|||||||
exclude group: "commons-logging", module: "commons-logging"
|
exclude group: "commons-logging", module: "commons-logging"
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation ("org.apache.pdfbox:pdfbox:3.0.2") {
|
implementation ("org.apache.pdfbox:pdfbox:$pdfboxVersion") {
|
||||||
exclude group: "commons-logging", module: "commons-logging"
|
exclude group: "commons-logging", module: "commons-logging"
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation ("org.apache.pdfbox:xmpbox:3.0.2") {
|
implementation ("org.apache.pdfbox:xmpbox:$pdfboxVersion") {
|
||||||
exclude group: "commons-logging", module: "commons-logging"
|
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 "com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4"
|
||||||
|
|
||||||
implementation "org.bouncycastle:bcprov-jdk18on:1.78.1"
|
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
|
||||||
implementation "org.bouncycastle:bcpkix-jdk18on:1.78.1"
|
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
|
||||||
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
||||||
implementation "io.micrometer:micrometer-core:1.13.0"
|
implementation "io.micrometer:micrometer-core:1.13.3"
|
||||||
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
|
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
|
||||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||||
implementation "org.commonmark:commonmark:0.22.0"
|
implementation "org.commonmark:commonmark:0.22.0"
|
||||||
implementation "org.commonmark:commonmark-ext-gfm-tables:0.22.0"
|
implementation "org.commonmark:commonmark-ext-gfm-tables:0.22.0"
|
||||||
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
|
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
|
||||||
implementation "com.bucket4j:bucket4j_jdk17-core:8.12.1"
|
implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0"
|
||||||
|
implementation "com.fathzer:javaluator:3.0.4"
|
||||||
implementation "com.fathzer:javaluator:3.0.4"
|
|
||||||
|
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion")
|
developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion")
|
||||||
compileOnly "org.projectlombok:lombok:1.18.32"
|
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||||
annotationProcessor "org.projectlombok:lombok:1.18.32"
|
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||||
|
|
||||||
testImplementation 'org.mockito:mockito-inline:5.2.0'
|
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile).configureEach {
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
appVersion: 0.27.0
|
appVersion: 0.28.3
|
||||||
description: locally hosted web application that allows you to perform various operations
|
description: locally hosted web application that allows you to perform various operations
|
||||||
on PDF files
|
on PDF files
|
||||||
home: https://github.com/Stirling-Tools/Stirling-PDF
|
home: https://github.com/Stirling-Tools/Stirling-PDF
|
||||||
|
|||||||
@@ -181,7 +181,9 @@ ignore = [
|
|||||||
|
|
||||||
[pt_BR]
|
[pt_BR]
|
||||||
ignore = [
|
ignore = [
|
||||||
|
'changeMetadata.trapped',
|
||||||
'language.direction',
|
'language.direction',
|
||||||
|
'pipelineOptions.pipelineHeader',
|
||||||
]
|
]
|
||||||
|
|
||||||
[pt_PT]
|
[pt_PT]
|
||||||
|
|||||||
174
scripts/png_to_webp.py
Normal file
174
scripts/png_to_webp.py
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
"""
|
||||||
|
Author: Ludy87
|
||||||
|
Description: This script converts a PDF file to WebP images. It includes functionality to resize images if they exceed specified dimensions and handle conversion of PDF pages to WebP format.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
To convert a PDF file to WebP images with each page as a separate WebP file:
|
||||||
|
python script.py input.pdf output_directory
|
||||||
|
|
||||||
|
To convert a PDF file to a single WebP image:
|
||||||
|
python script.py input.pdf output_directory --single
|
||||||
|
|
||||||
|
To adjust the DPI resolution for rendering PDF pages:
|
||||||
|
python script.py input.pdf output_directory --dpi 150
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
from pdf2image import convert_from_path
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
def resize_image(input_image_path, output_image_path, max_size=(16383, 16383)):
|
||||||
|
"""
|
||||||
|
Resize the image if its dimensions exceed the maximum allowed size and save it as WebP.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
input_image_path : str
|
||||||
|
Path to the input image file.
|
||||||
|
output_image_path : str
|
||||||
|
Path where the output WebP image will be saved.
|
||||||
|
max_size : tuple of int, optional
|
||||||
|
Maximum allowed dimensions for the image (width, height). Default is (16383, 16383).
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Open the image
|
||||||
|
image = Image.open(input_image_path)
|
||||||
|
width, height = image.size
|
||||||
|
max_width, max_height = max_size
|
||||||
|
|
||||||
|
# Check if the image dimensions exceed the maximum allowed dimensions
|
||||||
|
if width > max_width or height > max_height:
|
||||||
|
# Calculate the scaling ratio
|
||||||
|
ratio = min(max_width / width, max_height / height)
|
||||||
|
new_width = int(width * ratio)
|
||||||
|
new_height = int(height * ratio)
|
||||||
|
|
||||||
|
# Resize the image
|
||||||
|
resized_image = image.resize((new_width, new_height), Image.LANCZOS)
|
||||||
|
resized_image.save(output_image_path, format="WEBP", quality=100)
|
||||||
|
print(
|
||||||
|
f"The image was successfully resized to ({new_width}, {new_height}) and saved as WebP: {output_image_path}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# If dimensions are within the allowed limits, save the image directly
|
||||||
|
image.save(output_image_path, format="WEBP", quality=100)
|
||||||
|
print(f"The image was successfully saved as WebP: {output_image_path}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An error occurred: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def convert_image_to_webp(input_image, output_file):
|
||||||
|
"""
|
||||||
|
Convert an image to WebP format, resizing it if it exceeds the maximum dimensions.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
input_image : str
|
||||||
|
Path to the input image file.
|
||||||
|
output_file : str
|
||||||
|
Path where the output WebP image will be saved.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
# Resize the image if it exceeds the maximum dimensions
|
||||||
|
resize_image(input_image, output_file, max_size=(16383, 16383))
|
||||||
|
|
||||||
|
|
||||||
|
def pdf_to_webp(pdf_path, output_dir, dpi=300):
|
||||||
|
"""
|
||||||
|
Convert each page of a PDF file to WebP images.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pdf_path : str
|
||||||
|
Path to the input PDF file.
|
||||||
|
output_dir : str
|
||||||
|
Directory where the WebP images will be saved.
|
||||||
|
dpi : int, optional
|
||||||
|
DPI resolution for rendering PDF pages. Default is 300.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
# Convert the PDF to a list of images
|
||||||
|
images = convert_from_path(pdf_path, dpi=dpi)
|
||||||
|
|
||||||
|
for page_number, image in enumerate(images):
|
||||||
|
# Define temporary PNG path
|
||||||
|
temp_png_path = os.path.join(output_dir, f"temp_page_{page_number + 1}.png")
|
||||||
|
image.save(temp_png_path, format="PNG")
|
||||||
|
|
||||||
|
# Define the output path for WebP
|
||||||
|
output_path = os.path.join(output_dir, f"page_{page_number + 1}.webp")
|
||||||
|
|
||||||
|
# Convert PNG to WebP
|
||||||
|
convert_image_to_webp(temp_png_path, output_path)
|
||||||
|
|
||||||
|
# Delete the temporary PNG file
|
||||||
|
os.remove(temp_png_path)
|
||||||
|
|
||||||
|
|
||||||
|
def main(pdf_image_path, output_dir, dpi=300, single_images_flag=False):
|
||||||
|
"""
|
||||||
|
Main function to handle conversion from PDF to WebP images.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pdf_image_path : str
|
||||||
|
Path to the input PDF file or image.
|
||||||
|
output_dir : str
|
||||||
|
Directory where the WebP images will be saved.
|
||||||
|
dpi : int, optional
|
||||||
|
DPI resolution for rendering PDF pages. Default is 300.
|
||||||
|
single_images_flag : bool, optional
|
||||||
|
If True, combine all pages into a single WebP image. Default is False.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
if single_images_flag:
|
||||||
|
# Combine all pages into a single WebP image
|
||||||
|
output_path = os.path.join(output_dir, "combined_image.webp")
|
||||||
|
convert_image_to_webp(pdf_image_path, output_path)
|
||||||
|
else:
|
||||||
|
# Convert each PDF page to a separate WebP image
|
||||||
|
pdf_to_webp(pdf_image_path, output_dir, dpi)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Convert a PDF file to WebP images.")
|
||||||
|
parser.add_argument("pdf_path", help="The path to the input PDF file.")
|
||||||
|
parser.add_argument(
|
||||||
|
"output_dir", help="The directory where the WebP images should be saved."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--dpi",
|
||||||
|
type=int,
|
||||||
|
default=300,
|
||||||
|
help="The DPI resolution for rendering the PDF pages (default: 300).",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--single",
|
||||||
|
action="store_true",
|
||||||
|
help="Combine all pages into a single WebP image.",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
os.makedirs(args.output_dir, exist_ok=True)
|
||||||
|
main(
|
||||||
|
args.pdf_path,
|
||||||
|
args.output_dir,
|
||||||
|
dpi=args.dpi,
|
||||||
|
single_images_flag=args.single,
|
||||||
|
)
|
||||||
@@ -65,6 +65,7 @@ public class SPdfApplication {
|
|||||||
public static void main(String[] args) throws IOException, InterruptedException {
|
public static void main(String[] args) throws IOException, InterruptedException {
|
||||||
|
|
||||||
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
||||||
|
app.setAdditionalProfiles("default");
|
||||||
app.addInitializers(new ConfigInitializer());
|
app.addInitializers(new ConfigInitializer());
|
||||||
Map<String, String> propertyFiles = new HashMap<>();
|
Map<String, String> propertyFiles = new HashMap<>();
|
||||||
|
|
||||||
|
|||||||
@@ -126,13 +126,12 @@ public class AppConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "directoryFilter")
|
@Bean(name = "directoryFilter")
|
||||||
public Predicate<Path> processPDFOnlyFilter() {
|
public Predicate<Path> processOnlyFiles() {
|
||||||
return path -> {
|
return path -> {
|
||||||
if (Files.isDirectory(path)) {
|
if (Files.isDirectory(path)) {
|
||||||
return !path.toString().contains("processing");
|
return !path.toString().contains("processing");
|
||||||
} else {
|
} else {
|
||||||
String fileName = path.getFileName().toString();
|
return true;
|
||||||
return fileName.endsWith(".pdf");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.simpleyaml.configuration.comments.CommentType;
|
import org.simpleyaml.configuration.comments.CommentType;
|
||||||
import org.simpleyaml.configuration.file.YamlFile;
|
import org.simpleyaml.configuration.file.YamlFile;
|
||||||
|
import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
|
||||||
|
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.ApplicationContextInitializer;
|
import org.springframework.context.ApplicationContextInitializer;
|
||||||
@@ -71,9 +73,17 @@ public class ConfigInitializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
final YamlFile settingsTemplateFile = new YamlFile(tempTemplatePath.toFile());
|
final YamlFile settingsTemplateFile = new YamlFile(tempTemplatePath.toFile());
|
||||||
|
DumperOptions yamlOptionsSettingsTemplateFile =
|
||||||
|
((SimpleYamlImplementation) settingsTemplateFile.getImplementation())
|
||||||
|
.getDumperOptions();
|
||||||
|
yamlOptionsSettingsTemplateFile.setSplitLines(false);
|
||||||
settingsTemplateFile.loadWithComments();
|
settingsTemplateFile.loadWithComments();
|
||||||
|
|
||||||
final YamlFile settingsFile = new YamlFile(settingsPath.toFile());
|
final YamlFile settingsFile = new YamlFile(settingsPath.toFile());
|
||||||
|
DumperOptions yamlOptionsSettingsFile =
|
||||||
|
((SimpleYamlImplementation) settingsFile.getImplementation())
|
||||||
|
.getDumperOptions();
|
||||||
|
yamlOptionsSettingsFile.setSplitLines(false);
|
||||||
settingsFile.loadWithComments();
|
settingsFile.loadWithComments();
|
||||||
|
|
||||||
// Load headers and comments
|
// Load headers and comments
|
||||||
@@ -81,6 +91,10 @@ public class ConfigInitializer
|
|||||||
|
|
||||||
// Create a new file for temporary settings
|
// Create a new file for temporary settings
|
||||||
final YamlFile tempSettingFile = new YamlFile(settingsPath.toFile());
|
final YamlFile tempSettingFile = new YamlFile(settingsPath.toFile());
|
||||||
|
DumperOptions yamlOptionsTempSettingFile =
|
||||||
|
((SimpleYamlImplementation) tempSettingFile.getImplementation())
|
||||||
|
.getDumperOptions();
|
||||||
|
yamlOptionsTempSettingFile.setSplitLines(false);
|
||||||
tempSettingFile.createNewFile(true);
|
tempSettingFile.createNewFile(true);
|
||||||
tempSettingFile.setHeader(header);
|
tempSettingFile.setHeader(header);
|
||||||
|
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ public class EndpointConfiguration {
|
|||||||
addEndpointToGroup("Python", REMOVE_BLANKS);
|
addEndpointToGroup("Python", REMOVE_BLANKS);
|
||||||
addEndpointToGroup("Python", "html-to-pdf");
|
addEndpointToGroup("Python", "html-to-pdf");
|
||||||
addEndpointToGroup("Python", "url-to-pdf");
|
addEndpointToGroup("Python", "url-to-pdf");
|
||||||
|
addEndpointToGroup("Python", "pdf-to-img");
|
||||||
|
|
||||||
// openCV
|
// openCV
|
||||||
addEndpointToGroup("OpenCV", "extract-image-scans");
|
addEndpointToGroup("OpenCV", "extract-image-scans");
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ package stirling.software.SPDF.config.security;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.authentication.DisabledException;
|
||||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||||
import org.springframework.security.authentication.LockedException;
|
import org.springframework.security.authentication.LockedException;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
@@ -15,17 +14,16 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
|
|||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||||
|
|
||||||
private LoginAttemptService loginAttemptService;
|
private LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
private static final Logger logger =
|
|
||||||
LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);
|
|
||||||
|
|
||||||
public CustomAuthenticationFailureHandler(
|
public CustomAuthenticationFailureHandler(
|
||||||
final LoginAttemptService loginAttemptService, UserService userService) {
|
final LoginAttemptService loginAttemptService, UserService userService) {
|
||||||
this.loginAttemptService = loginAttemptService;
|
this.loginAttemptService = loginAttemptService;
|
||||||
@@ -39,14 +37,17 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
|
|||||||
AuthenticationException exception)
|
AuthenticationException exception)
|
||||||
throws IOException, ServletException {
|
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();
|
String ip = request.getRemoteAddr();
|
||||||
logger.error("Failed login attempt from IP: {}", ip);
|
log.error("Failed login attempt from IP: {}", ip);
|
||||||
|
|
||||||
String contextPath = request.getContextPath();
|
if (exception instanceof LockedException) {
|
||||||
|
getRedirectStrategy().sendRedirect(request, response, "/login?error=locked");
|
||||||
if (exception.getClass().isAssignableFrom(InternalAuthenticationServiceException.class)
|
|
||||||
|| "Password must not be null".equalsIgnoreCase(exception.getMessage())) {
|
|
||||||
response.sendRedirect(contextPath + "/login?error=oauth2AuthenticationError");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,20 +55,25 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
|
|||||||
Optional<User> optUser = userService.findByUsernameIgnoreCase(username);
|
Optional<User> optUser = userService.findByUsernameIgnoreCase(username);
|
||||||
|
|
||||||
if (username != null && optUser.isPresent() && !isDemoUser(optUser)) {
|
if (username != null && optUser.isPresent() && !isDemoUser(optUser)) {
|
||||||
logger.info(
|
log.info(
|
||||||
"Remaining attempts for user {}: {}",
|
"Remaining attempts for user {}: {}",
|
||||||
optUser.get().getUsername(),
|
username,
|
||||||
loginAttemptService.getRemainingAttempts(username));
|
loginAttemptService.getRemainingAttempts(username));
|
||||||
loginAttemptService.loginFailed(username);
|
loginAttemptService.loginFailed(username);
|
||||||
if (loginAttemptService.isBlocked(username)
|
if (loginAttemptService.isBlocked(username) || exception instanceof LockedException) {
|
||||||
|| exception.getClass().isAssignableFrom(LockedException.class)) {
|
getRedirectStrategy().sendRedirect(request, response, "/login?error=locked");
|
||||||
response.sendRedirect(contextPath + "/login?error=locked");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)
|
if (exception instanceof BadCredentialsException
|
||||||
|| exception.getClass().isAssignableFrom(UsernameNotFoundException.class)) {
|
|| exception instanceof UsernameNotFoundException) {
|
||||||
response.sendRedirect(contextPath + "/login?error=badcredentials");
|
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");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,15 +10,20 @@ import jakarta.servlet.ServletException;
|
|||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class CustomAuthenticationSuccessHandler
|
public class CustomAuthenticationSuccessHandler
|
||||||
extends SavedRequestAwareAuthenticationSuccessHandler {
|
extends SavedRequestAwareAuthenticationSuccessHandler {
|
||||||
|
|
||||||
private LoginAttemptService loginAttemptService;
|
private LoginAttemptService loginAttemptService;
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
public CustomAuthenticationSuccessHandler(LoginAttemptService loginAttemptService) {
|
public CustomAuthenticationSuccessHandler(
|
||||||
|
LoginAttemptService loginAttemptService, UserService userService) {
|
||||||
this.loginAttemptService = loginAttemptService;
|
this.loginAttemptService = loginAttemptService;
|
||||||
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -27,6 +32,10 @@ public class CustomAuthenticationSuccessHandler
|
|||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
||||||
String userName = request.getParameter("username");
|
String userName = request.getParameter("username");
|
||||||
|
if (userService.isUserDisabled(userName)) {
|
||||||
|
getRedirectStrategy().sendRedirect(request, response, "/logout?userIsDisabled=true");
|
||||||
|
return;
|
||||||
|
}
|
||||||
loginAttemptService.loginSucceeded(userName);
|
loginAttemptService.loginSucceeded(userName);
|
||||||
|
|
||||||
// Get the saved request
|
// Get the saved request
|
||||||
|
|||||||
@@ -2,32 +2,26 @@ package stirling.software.SPDF.config.security;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.session.SessionRegistry;
|
|
||||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
|
||||||
|
|
||||||
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||||
|
|
||||||
@Autowired SessionRegistry sessionRegistry;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLogoutSuccess(
|
public void onLogoutSuccess(
|
||||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
HttpSession session = request.getSession(false);
|
|
||||||
if (session != null) {
|
if (request.getParameter("userIsDisabled") != null) {
|
||||||
String sessionId = session.getId();
|
getRedirectStrategy()
|
||||||
sessionRegistry.removeSessionInformation(sessionId);
|
.sendRedirect(request, response, "/login?erroroauth=userIsDisabled");
|
||||||
session.invalidate();
|
return;
|
||||||
logger.debug("Session invalidated: " + sessionId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response.sendRedirect(request.getContextPath() + "/login?logout=true");
|
getRedirectStrategy().sendRedirect(request, response, "/login?logout=true");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import java.nio.file.Paths;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.simpleyaml.configuration.file.YamlFile;
|
import org.simpleyaml.configuration.file.YamlFile;
|
||||||
|
import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
|
||||||
|
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -92,6 +94,9 @@ public class InitialSecuritySetup {
|
|||||||
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
|
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
|
||||||
|
|
||||||
final YamlFile settingsYml = new YamlFile(path.toFile());
|
final YamlFile settingsYml = new YamlFile(path.toFile());
|
||||||
|
DumperOptions yamlOptionssettingsYml =
|
||||||
|
((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions();
|
||||||
|
yamlOptionssettingsYml.setSplitLines(false);
|
||||||
|
|
||||||
settingsYml.loadWithComments();
|
settingsYml.loadWithComments();
|
||||||
|
|
||||||
|
|||||||
@@ -3,29 +3,32 @@ package stirling.software.SPDF.config.security;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.AttemptCounter;
|
import stirling.software.SPDF.model.AttemptCounter;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
public class LoginAttemptService {
|
public class LoginAttemptService {
|
||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
@Autowired private ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(LoginAttemptService.class);
|
|
||||||
|
|
||||||
private int MAX_ATTEMPT;
|
private int MAX_ATTEMPT;
|
||||||
private long ATTEMPT_INCREMENT_TIME;
|
private long ATTEMPT_INCREMENT_TIME;
|
||||||
private ConcurrentHashMap<String, AttemptCounter> attemptsCache;
|
private ConcurrentHashMap<String, AttemptCounter> attemptsCache;
|
||||||
|
private boolean isBlockedEnabled = true;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
MAX_ATTEMPT = applicationProperties.getSecurity().getLoginAttemptCount();
|
MAX_ATTEMPT = applicationProperties.getSecurity().getLoginAttemptCount();
|
||||||
|
if (MAX_ATTEMPT == -1) {
|
||||||
|
isBlockedEnabled = false;
|
||||||
|
log.info("Login attempt tracking is disabled.");
|
||||||
|
}
|
||||||
ATTEMPT_INCREMENT_TIME =
|
ATTEMPT_INCREMENT_TIME =
|
||||||
TimeUnit.MINUTES.toMillis(
|
TimeUnit.MINUTES.toMillis(
|
||||||
applicationProperties.getSecurity().getLoginResetTimeMinutes());
|
applicationProperties.getSecurity().getLoginResetTimeMinutes());
|
||||||
@@ -33,14 +36,16 @@ public class LoginAttemptService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void loginSucceeded(String key) {
|
public void loginSucceeded(String key) {
|
||||||
if (key == null || key.trim().isEmpty()) {
|
if (!isBlockedEnabled || key == null || key.trim().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
attemptsCache.remove(key.toLowerCase());
|
attemptsCache.remove(key.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loginFailed(String key) {
|
public void loginFailed(String key) {
|
||||||
if (key == null || key.trim().isEmpty()) return;
|
if (!isBlockedEnabled || key == null || key.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
||||||
if (attemptCounter == null) {
|
if (attemptCounter == null) {
|
||||||
@@ -55,7 +60,9 @@ public class LoginAttemptService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBlocked(String key) {
|
public boolean isBlocked(String key) {
|
||||||
if (key == null || key.trim().isEmpty()) return false;
|
if (!isBlockedEnabled || key == null || key.trim().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
||||||
if (attemptCounter == null) {
|
if (attemptCounter == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -65,7 +72,9 @@ public class LoginAttemptService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getRemainingAttempts(String key) {
|
public int getRemainingAttempts(String key) {
|
||||||
if (key == null || key.trim().isEmpty()) return MAX_ATTEMPT;
|
if (!isBlockedEnabled || key == null || key.trim().isEmpty()) {
|
||||||
|
return Integer.MAX_VALUE; // Arbitrarily high number if tracking is disabled
|
||||||
|
}
|
||||||
|
|
||||||
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
|
||||||
if (attemptCounter == null) {
|
if (attemptCounter == null) {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
@@ -18,8 +17,6 @@ import org.springframework.security.config.http.SessionCreationPolicy;
|
|||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
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.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
@@ -37,6 +34,7 @@ import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationF
|
|||||||
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
|
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
|
||||||
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2LogoutSuccessHandler;
|
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2LogoutSuccessHandler;
|
||||||
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
|
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;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
|
||||||
@@ -47,7 +45,7 @@ import stirling.software.SPDF.model.provider.KeycloakProvider;
|
|||||||
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity()
|
@EnableWebSecurity
|
||||||
@EnableMethodSecurity
|
@EnableMethodSecurity
|
||||||
public class SecurityConfiguration {
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
@@ -73,11 +71,7 @@ public class SecurityConfiguration {
|
|||||||
@Autowired private LoginAttemptService loginAttemptService;
|
@Autowired private LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
@Autowired private FirstLoginFilter firstLoginFilter;
|
@Autowired private FirstLoginFilter firstLoginFilter;
|
||||||
|
@Autowired private SessionPersistentRegistry sessionRegistry;
|
||||||
@Bean
|
|
||||||
public SessionRegistry sessionRegistry() {
|
|
||||||
return new SessionRegistryImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
@@ -94,7 +88,7 @@ public class SecurityConfiguration {
|
|||||||
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
|
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
|
||||||
.maximumSessions(10)
|
.maximumSessions(10)
|
||||||
.maxSessionsPreventsLogin(false)
|
.maxSessionsPreventsLogin(false)
|
||||||
.sessionRegistry(sessionRegistry())
|
.sessionRegistry(sessionRegistry)
|
||||||
.expiredUrl("/login?logout=true"));
|
.expiredUrl("/login?logout=true"));
|
||||||
|
|
||||||
http.formLogin(
|
http.formLogin(
|
||||||
@@ -103,7 +97,7 @@ public class SecurityConfiguration {
|
|||||||
.loginPage("/login")
|
.loginPage("/login")
|
||||||
.successHandler(
|
.successHandler(
|
||||||
new CustomAuthenticationSuccessHandler(
|
new CustomAuthenticationSuccessHandler(
|
||||||
loginAttemptService))
|
loginAttemptService, userService))
|
||||||
.defaultSuccessUrl("/")
|
.defaultSuccessUrl("/")
|
||||||
.failureHandler(
|
.failureHandler(
|
||||||
new CustomAuthenticationFailureHandler(
|
new CustomAuthenticationFailureHandler(
|
||||||
@@ -155,12 +149,15 @@ public class SecurityConfiguration {
|
|||||||
})
|
})
|
||||||
.permitAll()
|
.permitAll()
|
||||||
.anyRequest()
|
.anyRequest()
|
||||||
.authenticated())
|
.authenticated());
|
||||||
.authenticationProvider(authenticationProvider());
|
|
||||||
|
|
||||||
// Handle OAUTH2 Logins
|
// Handle OAUTH2 Logins
|
||||||
if (applicationProperties.getSecurity().getOAUTH2() != null
|
if (applicationProperties.getSecurity().getOAUTH2() != null
|
||||||
&& applicationProperties.getSecurity().getOAUTH2().getEnabled()) {
|
&& applicationProperties.getSecurity().getOAUTH2().getEnabled()
|
||||||
|
&& !applicationProperties
|
||||||
|
.getSecurity()
|
||||||
|
.getLoginMethod()
|
||||||
|
.equalsIgnoreCase("normal")) {
|
||||||
|
|
||||||
http.oauth2Login(
|
http.oauth2Login(
|
||||||
oauth2 ->
|
oauth2 ->
|
||||||
@@ -191,10 +188,8 @@ public class SecurityConfiguration {
|
|||||||
.logout(
|
.logout(
|
||||||
logout ->
|
logout ->
|
||||||
logout.logoutSuccessHandler(
|
logout.logoutSuccessHandler(
|
||||||
new CustomOAuth2LogoutSuccessHandler(
|
new CustomOAuth2LogoutSuccessHandler(
|
||||||
this.applicationProperties,
|
applicationProperties)));
|
||||||
sessionRegistry()))
|
|
||||||
.invalidateHttpSession(true));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
http.csrf(csrf -> csrf.disable())
|
http.csrf(csrf -> csrf.disable())
|
||||||
@@ -382,14 +377,6 @@ public class SecurityConfiguration {
|
|||||||
return new IPRateLimitingFilter(maxRequestsPerIp, maxRequestsPerIp);
|
return new IPRateLimitingFilter(maxRequestsPerIp, maxRequestsPerIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
public DaoAuthenticationProvider authenticationProvider() {
|
|
||||||
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
|
||||||
authProvider.setUserDetailsService(userDetailsService);
|
|
||||||
authProvider.setPasswordEncoder(passwordEncoder());
|
|
||||||
return authProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public PersistentTokenRepository persistentTokenRepository() {
|
public PersistentTokenRepository persistentTokenRepository() {
|
||||||
return new JPATokenRepositoryImpl();
|
return new JPATokenRepositoryImpl();
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
@@ -8,9 +11,11 @@ import org.springframework.context.annotation.Lazy;
|
|||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
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.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
@@ -18,15 +23,17 @@ import jakarta.servlet.FilterChain;
|
|||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||||
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
|
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
|
||||||
|
import stirling.software.SPDF.model.User;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class UserAuthenticationFilter extends OncePerRequestFilter {
|
public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
@Autowired private UserDetailsService userDetailsService;
|
|
||||||
|
|
||||||
@Autowired @Lazy private UserService userService;
|
@Autowired @Lazy private UserService userService;
|
||||||
|
|
||||||
|
@Autowired private SessionPersistentRegistry sessionPersistentRegistry;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("loginEnabled")
|
@Qualifier("loginEnabled")
|
||||||
public boolean loginEnabledValue;
|
public boolean loginEnabledValue;
|
||||||
@@ -51,15 +58,20 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
try {
|
try {
|
||||||
// Use API key to authenticate. This requires you to have an authentication
|
// Use API key to authenticate. This requires you to have an authentication
|
||||||
// provider for API keys.
|
// provider for API keys.
|
||||||
UserDetails userDetails = userService.loadUserByApiKey(apiKey);
|
Optional<User> user = userService.getUserByApiKey(apiKey);
|
||||||
if (userDetails == null) {
|
if (!user.isPresent()) {
|
||||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
response.getWriter().write("Invalid API Key.");
|
response.getWriter().write("Invalid API Key.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
authentication =
|
List<SimpleGrantedAuthority> authorities =
|
||||||
new ApiKeyAuthenticationToken(
|
user.get().getAuthorities().stream()
|
||||||
userDetails, apiKey, userDetails.getAuthorities());
|
.map(
|
||||||
|
authority ->
|
||||||
|
new SimpleGrantedAuthority(
|
||||||
|
authority.getAuthority()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
authentication = new ApiKeyAuthenticationToken(user.get(), apiKey, authorities);
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
} catch (AuthenticationException e) {
|
} catch (AuthenticationException e) {
|
||||||
// If API key authentication fails, deny the request
|
// If API key authentication fails, deny the request
|
||||||
@@ -87,6 +99,43 @@ 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);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
package stirling.software.SPDF.config.security;
|
package stirling.software.SPDF.config.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
@@ -15,12 +7,14 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
|
|||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
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.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import stirling.software.SPDF.config.DatabaseBackupInterface;
|
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.controller.api.pipeline.UserServiceInterface;
|
||||||
import stirling.software.SPDF.model.AuthenticationType;
|
import stirling.software.SPDF.model.AuthenticationType;
|
||||||
import stirling.software.SPDF.model.Authority;
|
import stirling.software.SPDF.model.Authority;
|
||||||
@@ -29,6 +23,10 @@ import stirling.software.SPDF.model.User;
|
|||||||
import stirling.software.SPDF.repository.AuthorityRepository;
|
import stirling.software.SPDF.repository.AuthorityRepository;
|
||||||
import stirling.software.SPDF.repository.UserRepository;
|
import stirling.software.SPDF.repository.UserRepository;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class UserService implements UserServiceInterface {
|
public class UserService implements UserServiceInterface {
|
||||||
|
|
||||||
@@ -40,6 +38,8 @@ public class UserService implements UserServiceInterface {
|
|||||||
|
|
||||||
@Autowired private MessageSource messageSource;
|
@Autowired private MessageSource messageSource;
|
||||||
|
|
||||||
|
@Autowired private SessionPersistentRegistry sessionRegistry;
|
||||||
|
|
||||||
@Autowired DatabaseBackupInterface databaseBackupHelper;
|
@Autowired DatabaseBackupInterface databaseBackupHelper;
|
||||||
|
|
||||||
// Handle OAUTH2 login and user auto creation.
|
// Handle OAUTH2 login and user auto creation.
|
||||||
@@ -48,7 +48,7 @@ public class UserService implements UserServiceInterface {
|
|||||||
if (!isUsernameValid(username)) {
|
if (!isUsernameValid(username)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Optional<User> existingUser = userRepository.findByUsernameIgnoreCase(username);
|
Optional<User> existingUser = findByUsernameIgnoreCase(username);
|
||||||
if (existingUser.isPresent()) {
|
if (existingUser.isPresent()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -60,8 +60,8 @@ public class UserService implements UserServiceInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Authentication getAuthentication(String apiKey) {
|
public Authentication getAuthentication(String apiKey) {
|
||||||
User user = getUserByApiKey(apiKey);
|
Optional<User> user = getUserByApiKey(apiKey);
|
||||||
if (user == null) {
|
if (!user.isPresent()) {
|
||||||
throw new UsernameNotFoundException("API key is not valid");
|
throw new UsernameNotFoundException("API key is not valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ public class UserService implements UserServiceInterface {
|
|||||||
return new UsernamePasswordAuthenticationToken(
|
return new UsernamePasswordAuthenticationToken(
|
||||||
user, // principal (typically the user)
|
user, // principal (typically the user)
|
||||||
null, // credentials (we don't expose the password or API key here)
|
null, // credentials (we don't expose the password or API key here)
|
||||||
getAuthorities(user) // user's authorities (roles/permissions)
|
getAuthorities(user.get()) // user's authorities (roles/permissions)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,18 +84,17 @@ public class UserService implements UserServiceInterface {
|
|||||||
String apiKey;
|
String apiKey;
|
||||||
do {
|
do {
|
||||||
apiKey = UUID.randomUUID().toString();
|
apiKey = UUID.randomUUID().toString();
|
||||||
} while (userRepository.findByApiKey(apiKey) != null); // Ensure uniqueness
|
} while (userRepository.findByApiKey(apiKey).isPresent()); // Ensure uniqueness
|
||||||
return apiKey;
|
return apiKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public User addApiKeyToUser(String username) {
|
public User addApiKeyToUser(String username) {
|
||||||
User user =
|
Optional<User> user = findByUsernameIgnoreCase(username);
|
||||||
userRepository
|
if (user.isPresent()) {
|
||||||
.findByUsernameIgnoreCase(username)
|
user.get().setApiKey(generateApiKey());
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
return userRepository.save(user.get());
|
||||||
|
}
|
||||||
user.setApiKey(generateApiKey());
|
throw new UsernameNotFoundException("User not found");
|
||||||
return userRepository.save(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public User refreshApiKeyForUser(String username) {
|
public User refreshApiKeyForUser(String username) {
|
||||||
@@ -104,39 +103,40 @@ public class UserService implements UserServiceInterface {
|
|||||||
|
|
||||||
public String getApiKeyForUser(String username) {
|
public String getApiKeyForUser(String username) {
|
||||||
User user =
|
User user =
|
||||||
userRepository
|
findByUsernameIgnoreCase(username)
|
||||||
.findByUsernameIgnoreCase(username)
|
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||||
return user.getApiKey();
|
return user.getApiKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isValidApiKey(String apiKey) {
|
public boolean isValidApiKey(String apiKey) {
|
||||||
return userRepository.findByApiKey(apiKey) != null;
|
return userRepository.findByApiKey(apiKey).isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public User getUserByApiKey(String apiKey) {
|
public Optional<User> getUserByApiKey(String apiKey) {
|
||||||
return userRepository.findByApiKey(apiKey);
|
return userRepository.findByApiKey(apiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserDetails loadUserByApiKey(String apiKey) {
|
public Optional<User> loadUserByApiKey(String apiKey) {
|
||||||
User user = userRepository.findByApiKey(apiKey);
|
Optional<User> user = userRepository.findByApiKey(apiKey);
|
||||||
if (user != null) {
|
|
||||||
// Convert your User entity to a UserDetails object with authorities
|
if (user.isPresent()) {
|
||||||
return new org.springframework.security.core.userdetails.User(
|
return user;
|
||||||
user.getUsername(),
|
|
||||||
user.getPassword(), // you might not need this for API key auth
|
|
||||||
getAuthorities(user));
|
|
||||||
}
|
}
|
||||||
return null; // or throw an exception
|
return null; // or throw an exception
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validateApiKeyForUser(String username, String apiKey) {
|
public boolean validateApiKeyForUser(String username, String apiKey) {
|
||||||
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
|
Optional<User> userOpt = findByUsernameIgnoreCase(username);
|
||||||
return userOpt.isPresent() && apiKey.equals(userOpt.get().getApiKey());
|
return userOpt.isPresent() && apiKey.equals(userOpt.get().getApiKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveUser(String username, AuthenticationType authenticationType)
|
public void saveUser(String username, AuthenticationType authenticationType)
|
||||||
throws IllegalArgumentException, IOException {
|
throws IllegalArgumentException, IOException {
|
||||||
|
saveUser(username, authenticationType, Role.USER.getRoleId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveUser(String username, AuthenticationType authenticationType, String role)
|
||||||
|
throws IllegalArgumentException, IOException {
|
||||||
if (!isUsernameValid(username)) {
|
if (!isUsernameValid(username)) {
|
||||||
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||||
}
|
}
|
||||||
@@ -144,7 +144,7 @@ public class UserService implements UserServiceInterface {
|
|||||||
user.setUsername(username);
|
user.setUsername(username);
|
||||||
user.setEnabled(true);
|
user.setEnabled(true);
|
||||||
user.setFirstLogin(false);
|
user.setFirstLogin(false);
|
||||||
user.addAuthority(new Authority(Role.USER.getRoleId(), user));
|
user.addAuthority(new Authority(role, user));
|
||||||
user.setAuthenticationType(authenticationType);
|
user.setAuthenticationType(authenticationType);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
databaseBackupHelper.exportDatabase();
|
databaseBackupHelper.exportDatabase();
|
||||||
@@ -186,7 +186,7 @@ public class UserService implements UserServiceInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deleteUser(String username) {
|
public void deleteUser(String username) {
|
||||||
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
|
Optional<User> userOpt = findByUsernameIgnoreCase(username);
|
||||||
if (userOpt.isPresent()) {
|
if (userOpt.isPresent()) {
|
||||||
for (Authority authority : userOpt.get().getAuthorities()) {
|
for (Authority authority : userOpt.get().getAuthorities()) {
|
||||||
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
||||||
@@ -195,21 +195,20 @@ public class UserService implements UserServiceInterface {
|
|||||||
}
|
}
|
||||||
userRepository.delete(userOpt.get());
|
userRepository.delete(userOpt.get());
|
||||||
}
|
}
|
||||||
|
invalidateUserSessions(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean usernameExists(String username) {
|
public boolean usernameExists(String username) {
|
||||||
return userRepository.findByUsername(username).isPresent();
|
return findByUsername(username).isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean usernameExistsIgnoreCase(String username) {
|
public boolean usernameExistsIgnoreCase(String username) {
|
||||||
return userRepository.findByUsernameIgnoreCase(username).isPresent();
|
return findByUsernameIgnoreCase(username).isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasUsers() {
|
public boolean hasUsers() {
|
||||||
long userCount = userRepository.count();
|
long userCount = userRepository.count();
|
||||||
if (userRepository
|
if (findByUsernameIgnoreCase(Role.INTERNAL_API_USER.getRoleId()).isPresent()) {
|
||||||
.findByUsernameIgnoreCase(Role.INTERNAL_API_USER.getRoleId())
|
|
||||||
.isPresent()) {
|
|
||||||
userCount -= 1;
|
userCount -= 1;
|
||||||
}
|
}
|
||||||
return userCount > 0;
|
return userCount > 0;
|
||||||
@@ -217,7 +216,7 @@ public class UserService implements UserServiceInterface {
|
|||||||
|
|
||||||
public void updateUserSettings(String username, Map<String, String> updates)
|
public void updateUserSettings(String username, Map<String, String> updates)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
|
Optional<User> userOpt = findByUsernameIgnoreCaseWithSettings(username);
|
||||||
if (userOpt.isPresent()) {
|
if (userOpt.isPresent()) {
|
||||||
User user = userOpt.get();
|
User user = userOpt.get();
|
||||||
Map<String, String> settingsMap = user.getSettings();
|
Map<String, String> settingsMap = user.getSettings();
|
||||||
@@ -242,6 +241,10 @@ public class UserService implements UserServiceInterface {
|
|||||||
return userRepository.findByUsernameIgnoreCase(username);
|
return userRepository.findByUsernameIgnoreCase(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<User> findByUsernameIgnoreCaseWithSettings(String username) {
|
||||||
|
return userRepository.findByUsernameIgnoreCaseWithSettings(username);
|
||||||
|
}
|
||||||
|
|
||||||
public Authority findRole(User user) {
|
public Authority findRole(User user) {
|
||||||
return authorityRepository.findByUserId(user.getId());
|
return authorityRepository.findByUserId(user.getId());
|
||||||
}
|
}
|
||||||
@@ -268,10 +271,17 @@ public class UserService implements UserServiceInterface {
|
|||||||
databaseBackupHelper.exportDatabase();
|
databaseBackupHelper.exportDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void changeRole(User user, String newRole) {
|
public void changeRole(User user, String newRole) throws IOException {
|
||||||
Authority userAuthority = this.findRole(user);
|
Authority userAuthority = this.findRole(user);
|
||||||
userAuthority.setAuthority(newRole);
|
userAuthority.setAuthority(newRole);
|
||||||
authorityRepository.save(userAuthority);
|
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) {
|
public boolean isPasswordCorrect(User user, String currentPassword) {
|
||||||
@@ -295,14 +305,40 @@ public class UserService implements UserServiceInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPassword(String username) {
|
public boolean hasPassword(String username) {
|
||||||
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
|
Optional<User> user = findByUsernameIgnoreCase(username);
|
||||||
return user.isPresent() && user.get().hasPassword();
|
return user.isPresent() && user.get().hasPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAuthenticationTypeByUsername(
|
public boolean isAuthenticationTypeByUsername(
|
||||||
String username, AuthenticationType authenticationType) {
|
String username, AuthenticationType authenticationType) {
|
||||||
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
|
Optional<User> user = findByUsernameIgnoreCase(username);
|
||||||
return user.isPresent()
|
return user.isPresent()
|
||||||
&& authenticationType.name().equalsIgnoreCase(user.get().getAuthenticationType());
|
&& 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package stirling.software.SPDF.config.security.oauth2;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.springframework.security.authentication.DisabledException;
|
||||||
import org.springframework.security.authentication.LockedException;
|
import org.springframework.security.authentication.LockedException;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||||
@@ -13,19 +13,34 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
|
|||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class CustomOAuth2AuthenticationFailureHandler
|
public class CustomOAuth2AuthenticationFailureHandler
|
||||||
extends SimpleUrlAuthenticationFailureHandler {
|
extends SimpleUrlAuthenticationFailureHandler {
|
||||||
|
|
||||||
private static final Logger logger =
|
|
||||||
LoggerFactory.getLogger(CustomOAuth2AuthenticationFailureHandler.class);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAuthenticationFailure(
|
public void onAuthenticationFailure(
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
AuthenticationException exception)
|
AuthenticationException exception)
|
||||||
throws IOException, ServletException {
|
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) {
|
if (exception instanceof OAuth2AuthenticationException) {
|
||||||
OAuth2Error error = ((OAuth2AuthenticationException) exception).getError();
|
OAuth2Error error = ((OAuth2AuthenticationException) exception).getError();
|
||||||
|
|
||||||
@@ -34,17 +49,13 @@ public class CustomOAuth2AuthenticationFailureHandler
|
|||||||
if (error.getErrorCode().equals("Password must not be null")) {
|
if (error.getErrorCode().equals("Password must not be null")) {
|
||||||
errorCode = "userAlreadyExistsWeb";
|
errorCode = "userAlreadyExistsWeb";
|
||||||
}
|
}
|
||||||
logger.error("OAuth2 Authentication error: " + errorCode);
|
log.error("OAuth2 Authentication error: " + errorCode);
|
||||||
|
log.error("OAuth2AuthenticationException", exception);
|
||||||
getRedirectStrategy()
|
getRedirectStrategy()
|
||||||
.sendRedirect(request, response, "/logout?erroroauth=" + errorCode);
|
.sendRedirect(request, response, "/logout?erroroauth=" + errorCode);
|
||||||
return;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,9 @@ package stirling.software.SPDF.config.security.oauth2;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.security.authentication.LockedException;
|
import org.springframework.security.authentication.LockedException;
|
||||||
import org.springframework.security.core.Authentication;
|
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.oauth2.core.user.OAuth2User;
|
||||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||||
@@ -26,9 +25,6 @@ public class CustomOAuth2AuthenticationSuccessHandler
|
|||||||
|
|
||||||
private LoginAttemptService loginAttemptService;
|
private LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
private static final Logger logger =
|
|
||||||
LoggerFactory.getLogger(CustomOAuth2AuthenticationSuccessHandler.class);
|
|
||||||
|
|
||||||
private ApplicationProperties applicationProperties;
|
private ApplicationProperties applicationProperties;
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
@@ -46,6 +42,17 @@ public class CustomOAuth2AuthenticationSuccessHandler
|
|||||||
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
||||||
throws ServletException, IOException {
|
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
|
// Get the saved request
|
||||||
HttpSession session = request.getSession(false);
|
HttpSession session = request.getSession(false);
|
||||||
String contextPath = request.getContextPath();
|
String contextPath = request.getContextPath();
|
||||||
@@ -59,11 +66,8 @@ public class CustomOAuth2AuthenticationSuccessHandler
|
|||||||
// Redirect to the original destination
|
// Redirect to the original destination
|
||||||
super.onAuthenticationSuccess(request, response, authentication);
|
super.onAuthenticationSuccess(request, response, authentication);
|
||||||
} else {
|
} else {
|
||||||
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
|
|
||||||
OAUTH2 oAuth = applicationProperties.getSecurity().getOAUTH2();
|
OAUTH2 oAuth = applicationProperties.getSecurity().getOAUTH2();
|
||||||
|
|
||||||
String username = oauthUser.getName();
|
|
||||||
|
|
||||||
if (loginAttemptService.isBlocked(username)) {
|
if (loginAttemptService.isBlocked(username)) {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
|
session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
|
||||||
@@ -78,15 +82,21 @@ public class CustomOAuth2AuthenticationSuccessHandler
|
|||||||
&& oAuth.getAutoCreateUser()) {
|
&& oAuth.getAutoCreateUser()) {
|
||||||
response.sendRedirect(contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
|
response.sendRedirect(contextPath + "/logout?oauth2AuthenticationErrorWeb=true");
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
try {
|
try {
|
||||||
userService.processOAuth2PostLogin(username, oAuth.getAutoCreateUser());
|
if (oAuth.getBlockRegistration()
|
||||||
response.sendRedirect(contextPath + "/");
|
&& !userService.usernameExistsIgnoreCase(username)) {
|
||||||
return;
|
response.sendRedirect(contextPath + "/logout?oauth2_admin_blocked_user=true");
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
response.sendRedirect(contextPath + "/logout?invalidUsername=true");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (principal instanceof OAuth2User) {
|
||||||
|
userService.processOAuth2PostLogin(username, oAuth.getAutoCreateUser());
|
||||||
|
}
|
||||||
|
response.sendRedirect(contextPath + "/");
|
||||||
|
return;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
response.sendRedirect(contextPath + "/logout?invalidUsername=true");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,34 +2,26 @@ package stirling.software.SPDF.config.security.oauth2;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.security.core.Authentication;
|
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.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||||
import stirling.software.SPDF.model.Provider;
|
import stirling.software.SPDF.model.Provider;
|
||||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||||
import stirling.software.SPDF.utils.UrlUtils;
|
import stirling.software.SPDF.utils.UrlUtils;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||||
|
|
||||||
private static final Logger logger =
|
|
||||||
LoggerFactory.getLogger(CustomOAuth2LogoutSuccessHandler.class);
|
|
||||||
|
|
||||||
private final SessionRegistry sessionRegistry;
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
public CustomOAuth2LogoutSuccessHandler(
|
public CustomOAuth2LogoutSuccessHandler(ApplicationProperties applicationProperties) {
|
||||||
ApplicationProperties applicationProperties, SessionRegistry sessionRegistry) {
|
|
||||||
this.sessionRegistry = sessionRegistry;
|
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +34,15 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
|
|||||||
String issuer = null;
|
String issuer = null;
|
||||||
String clientId = 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();
|
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
||||||
|
|
||||||
if (authentication instanceof OAuth2AuthenticationToken) {
|
if (authentication instanceof OAuth2AuthenticationToken) {
|
||||||
@@ -53,9 +54,8 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
|
|||||||
issuer = provider.getIssuer();
|
issuer = provider.getIssuer();
|
||||||
clientId = provider.getClientId();
|
clientId = provider.getClientId();
|
||||||
} catch (UnsupportedProviderException e) {
|
} catch (UnsupportedProviderException e) {
|
||||||
logger.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
registrationId = oauth.getProvider() != null ? oauth.getProvider() : "";
|
registrationId = oauth.getProvider() != null ? oauth.getProvider() : "";
|
||||||
issuer = oauth.getIssuer();
|
issuer = oauth.getIssuer();
|
||||||
@@ -70,18 +70,16 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
|
|||||||
param = "erroroauth=" + sanitizeInput(errorMessage);
|
param = "erroroauth=" + sanitizeInput(errorMessage);
|
||||||
} else if (request.getParameter("oauth2AutoCreateDisabled") != null) {
|
} else if (request.getParameter("oauth2AutoCreateDisabled") != null) {
|
||||||
param = "error=oauth2AutoCreateDisabled";
|
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;
|
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()) {
|
switch (registrationId.toLowerCase()) {
|
||||||
case "keycloak":
|
case "keycloak":
|
||||||
// Add Keycloak specific logout URL if needed
|
// Add Keycloak specific logout URL if needed
|
||||||
@@ -92,13 +90,13 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
|
|||||||
+ clientId
|
+ clientId
|
||||||
+ "&post_logout_redirect_uri="
|
+ "&post_logout_redirect_uri="
|
||||||
+ response.encodeRedirectURL(redirect_url);
|
+ response.encodeRedirectURL(redirect_url);
|
||||||
logger.info("Redirecting to Keycloak logout URL: " + logoutUrl);
|
log.info("Redirecting to Keycloak logout URL: " + logoutUrl);
|
||||||
response.sendRedirect(logoutUrl);
|
response.sendRedirect(logoutUrl);
|
||||||
break;
|
break;
|
||||||
case "github":
|
case "github":
|
||||||
// Add GitHub specific logout URL if needed
|
// Add GitHub specific logout URL if needed
|
||||||
String githubLogoutUrl = "https://github.com/logout";
|
String githubLogoutUrl = "https://github.com/logout";
|
||||||
logger.info("Redirecting to GitHub logout URL: " + githubLogoutUrl);
|
log.info("Redirecting to GitHub logout URL: " + githubLogoutUrl);
|
||||||
response.sendRedirect(githubLogoutUrl);
|
response.sendRedirect(githubLogoutUrl);
|
||||||
break;
|
break;
|
||||||
case "google":
|
case "google":
|
||||||
@@ -106,13 +104,14 @@ public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHand
|
|||||||
// String googleLogoutUrl =
|
// String googleLogoutUrl =
|
||||||
// "https://accounts.google.com/Logout?continue=https://appengine.google.com/_ah/logout?continue="
|
// "https://accounts.google.com/Logout?continue=https://appengine.google.com/_ah/logout?continue="
|
||||||
// + response.encodeRedirectURL(redirect_url);
|
// + response.encodeRedirectURL(redirect_url);
|
||||||
// logger.info("Redirecting to Google logout URL: " + googleLogoutUrl);
|
log.info("Google does not have a specific logout URL");
|
||||||
|
// log.info("Redirecting to Google logout URL: " + googleLogoutUrl);
|
||||||
// response.sendRedirect(googleLogoutUrl);
|
// response.sendRedirect(googleLogoutUrl);
|
||||||
// break;
|
// break;
|
||||||
default:
|
default:
|
||||||
String redirectUrl = request.getContextPath() + "/login?" + param;
|
String defaultRedirectUrl = request.getContextPath() + "/login?" + param;
|
||||||
logger.info("Redirecting to default logout URL: " + redirectUrl);
|
log.info("Redirecting to default logout URL: " + defaultRedirectUrl);
|
||||||
response.sendRedirect(redirectUrl);
|
response.sendRedirect(defaultRedirectUrl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package stirling.software.SPDF.controller.api;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -12,8 +13,8 @@ import org.springframework.http.ResponseEntity;
|
|||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.session.SessionInformation;
|
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.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
@@ -30,6 +31,8 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import stirling.software.SPDF.config.security.UserService;
|
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.Role;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
import stirling.software.SPDF.model.api.user.UsernameAndPass;
|
import stirling.software.SPDF.model.api.user.UsernameAndPass;
|
||||||
@@ -41,6 +44,8 @@ public class UserController {
|
|||||||
|
|
||||||
@Autowired private UserService userService;
|
@Autowired private UserService userService;
|
||||||
|
|
||||||
|
@Autowired SessionPersistentRegistry sessionRegistry;
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
public String register(@ModelAttribute UsernameAndPass requestModel, Model model)
|
public String register(@ModelAttribute UsernameAndPass requestModel, Model model)
|
||||||
@@ -203,9 +208,10 @@ public class UserController {
|
|||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@PostMapping("/admin/saveUser")
|
@PostMapping("/admin/saveUser")
|
||||||
public RedirectView saveUser(
|
public RedirectView saveUser(
|
||||||
@RequestParam(name = "username") String username,
|
@RequestParam String username,
|
||||||
@RequestParam(name = "password") String password,
|
@RequestParam(name = "password", required = false) String password,
|
||||||
@RequestParam(name = "role") String role,
|
@RequestParam(name = "role") String role,
|
||||||
|
@RequestParam(name = "authType") String authType,
|
||||||
@RequestParam(name = "forceChange", required = false, defaultValue = "false")
|
@RequestParam(name = "forceChange", required = false, defaultValue = "false")
|
||||||
boolean forceChange)
|
boolean forceChange)
|
||||||
throws IllegalArgumentException, IOException {
|
throws IllegalArgumentException, IOException {
|
||||||
@@ -237,7 +243,15 @@ public class UserController {
|
|||||||
return new RedirectView("/addUsers?messageType=invalidRole", true);
|
return new RedirectView("/addUsers?messageType=invalidRole", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
userService.saveUser(username, password, role, forceChange);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
return new RedirectView(
|
return new RedirectView(
|
||||||
"/addUsers", true); // Redirect to account page after adding the user
|
"/addUsers", true); // Redirect to account page after adding the user
|
||||||
}
|
}
|
||||||
@@ -247,7 +261,8 @@ public class UserController {
|
|||||||
public RedirectView changeRole(
|
public RedirectView changeRole(
|
||||||
@RequestParam(name = "username") String username,
|
@RequestParam(name = "username") String username,
|
||||||
@RequestParam(name = "role") String role,
|
@RequestParam(name = "role") String role,
|
||||||
Authentication authentication) {
|
Authentication authentication)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
||||||
|
|
||||||
@@ -278,6 +293,60 @@ public class UserController {
|
|||||||
User user = userOpt.get();
|
User user = userOpt.get();
|
||||||
|
|
||||||
userService.changeRole(user, role);
|
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(
|
return new RedirectView(
|
||||||
"/addUsers", true); // Redirect to account page after adding the user
|
"/addUsers", true); // Redirect to account page after adding the user
|
||||||
}
|
}
|
||||||
@@ -285,7 +354,7 @@ public class UserController {
|
|||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@PostMapping("/admin/deleteUser/{username}")
|
@PostMapping("/admin/deleteUser/{username}")
|
||||||
public RedirectView deleteUser(
|
public RedirectView deleteUser(
|
||||||
@PathVariable(name = "username") String username, Authentication authentication) {
|
@PathVariable("username") String username, Authentication authentication) {
|
||||||
|
|
||||||
if (!userService.usernameExistsIgnoreCase(username)) {
|
if (!userService.usernameExistsIgnoreCase(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=deleteUsernameExists", true);
|
return new RedirectView("/addUsers?messageType=deleteUsernameExists", true);
|
||||||
@@ -298,27 +367,18 @@ public class UserController {
|
|||||||
if (currentUsername.equalsIgnoreCase(username)) {
|
if (currentUsername.equalsIgnoreCase(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=deleteCurrentUser", true);
|
return new RedirectView("/addUsers?messageType=deleteCurrentUser", true);
|
||||||
}
|
}
|
||||||
invalidateUserSessions(username);
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
userService.deleteUser(username);
|
userService.deleteUser(username);
|
||||||
return new RedirectView("/addUsers", true);
|
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')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
@PostMapping("/get-api-key")
|
@PostMapping("/get-api-key")
|
||||||
public ResponseEntity<String> getApiKey(Principal principal) {
|
public ResponseEntity<String> getApiKey(Principal principal) {
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
package stirling.software.SPDF.controller.api.converters;
|
package stirling.software.SPDF.controller.api.converters;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.pdfbox.rendering.ImageType;
|
import org.apache.pdfbox.rendering.ImageType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -20,7 +30,10 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
|
|
||||||
import stirling.software.SPDF.model.api.converters.ConvertToImageRequest;
|
import stirling.software.SPDF.model.api.converters.ConvertToImageRequest;
|
||||||
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
|
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
|
||||||
|
import stirling.software.SPDF.utils.CheckProgramInstall;
|
||||||
import stirling.software.SPDF.utils.PdfUtils;
|
import stirling.software.SPDF.utils.PdfUtils;
|
||||||
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
|
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -60,15 +73,87 @@ public class ConvertImgPDFController {
|
|||||||
result =
|
result =
|
||||||
PdfUtils.convertFromPdf(
|
PdfUtils.convertFromPdf(
|
||||||
pdfBytes,
|
pdfBytes,
|
||||||
imageFormat.toUpperCase(),
|
imageFormat.equalsIgnoreCase("webp") ? "png" : imageFormat.toUpperCase(),
|
||||||
colorTypeResult,
|
colorTypeResult,
|
||||||
singleImage,
|
singleImage,
|
||||||
Integer.valueOf(dpi),
|
Integer.valueOf(dpi),
|
||||||
filename);
|
filename);
|
||||||
|
|
||||||
if (result == null || result.length == 0) {
|
if (result == null || result.length == 0) {
|
||||||
logger.error("resultant bytes for {} is null, error converting ", filename);
|
logger.error("resultant bytes for {} is null, error converting ", filename);
|
||||||
}
|
}
|
||||||
|
if (imageFormat.equalsIgnoreCase("webp") && !CheckProgramInstall.isPythonAvailable()) {
|
||||||
|
throw new IOException("Python is not installed. Required for WebP conversion.");
|
||||||
|
} else if (imageFormat.equalsIgnoreCase("webp")
|
||||||
|
&& CheckProgramInstall.isPythonAvailable()) {
|
||||||
|
// Write the output stream to a temp file
|
||||||
|
Path tempFile = Files.createTempFile("temp_png", ".png");
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) {
|
||||||
|
fos.write(result);
|
||||||
|
fos.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
String pythonVersion = CheckProgramInstall.getAvailablePythonCommand();
|
||||||
|
|
||||||
|
List<String> command = new ArrayList<>();
|
||||||
|
command.add(pythonVersion);
|
||||||
|
command.add("./scripts/png_to_webp.py"); // Python script to handle the conversion
|
||||||
|
|
||||||
|
// Create a temporary directory for the output WebP files
|
||||||
|
Path tempOutputDir = Files.createTempDirectory("webp_output");
|
||||||
|
if (singleImage) {
|
||||||
|
// Run the Python script to convert PNG to WebP
|
||||||
|
command.add(tempFile.toString());
|
||||||
|
command.add(tempOutputDir.toString());
|
||||||
|
command.add("--single");
|
||||||
|
} else {
|
||||||
|
// Save the uploaded PDF to a temporary file
|
||||||
|
Path tempPdfPath = Files.createTempFile("temp_pdf", ".pdf");
|
||||||
|
file.transferTo(tempPdfPath.toFile());
|
||||||
|
// Run the Python script to convert PDF to WebP
|
||||||
|
command.add(tempPdfPath.toString());
|
||||||
|
command.add(tempOutputDir.toString());
|
||||||
|
}
|
||||||
|
command.add("--dpi");
|
||||||
|
command.add(dpi);
|
||||||
|
ProcessExecutorResult resultProcess =
|
||||||
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
|
||||||
|
.runCommandWithOutputHandling(command);
|
||||||
|
|
||||||
|
// Find all WebP files in the output directory
|
||||||
|
List<Path> webpFiles =
|
||||||
|
Files.walk(tempOutputDir)
|
||||||
|
.filter(path -> path.toString().endsWith(".webp"))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (webpFiles.isEmpty()) {
|
||||||
|
logger.error("No WebP files were created in: {}", tempOutputDir.toString());
|
||||||
|
throw new IOException("No WebP files were created. " + resultProcess.getMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bodyBytes = new byte[0];
|
||||||
|
|
||||||
|
if (webpFiles.size() == 1) {
|
||||||
|
// Return the single WebP file directly
|
||||||
|
Path webpFilePath = webpFiles.get(0);
|
||||||
|
bodyBytes = Files.readAllBytes(webpFilePath);
|
||||||
|
} else {
|
||||||
|
// Create a ZIP file containing all WebP images
|
||||||
|
ByteArrayOutputStream zipOutputStream = new ByteArrayOutputStream();
|
||||||
|
try (ZipOutputStream zos = new ZipOutputStream(zipOutputStream)) {
|
||||||
|
for (Path webpFile : webpFiles) {
|
||||||
|
zos.putNextEntry(new ZipEntry(webpFile.getFileName().toString()));
|
||||||
|
Files.copy(webpFile, zos);
|
||||||
|
zos.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bodyBytes = zipOutputStream.toByteArray();
|
||||||
|
}
|
||||||
|
// Clean up the temporary files
|
||||||
|
Files.deleteIfExists(tempFile);
|
||||||
|
if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile());
|
||||||
|
result = bodyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
if (singleImage) {
|
if (singleImage) {
|
||||||
String docName = filename + "." + imageFormat;
|
String docName = filename + "." + imageFormat;
|
||||||
MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat));
|
MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat));
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class AutoRenameController {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(AutoRenameController.class);
|
private static final Logger logger = LoggerFactory.getLogger(AutoRenameController.class);
|
||||||
|
|
||||||
private static final float TITLE_FONT_SIZE_THRESHOLD = 20.0f;
|
private static final float TITLE_FONT_SIZE_THRESHOLD = 20.0f;
|
||||||
private static final int LINE_LIMIT = 11;
|
private static final int LINE_LIMIT = 200;
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/auto-rename")
|
@PostMapping(consumes = "multipart/form-data", value = "/auto-rename")
|
||||||
@Operation(
|
@Operation(
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.api.misc.ExtractImageScansRequest;
|
import stirling.software.SPDF.model.api.misc.ExtractImageScansRequest;
|
||||||
|
import stirling.software.SPDF.utils.CheckProgramInstall;
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
@@ -76,6 +77,11 @@ public class ExtractImageScansController {
|
|||||||
Path tempZipFile = null;
|
Path tempZipFile = null;
|
||||||
List<Path> tempDirs = new ArrayList<>();
|
List<Path> tempDirs = new ArrayList<>();
|
||||||
|
|
||||||
|
if (!CheckProgramInstall.isPythonAvailable()) {
|
||||||
|
throw new IOException("Python is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String pythonVersion = CheckProgramInstall.getAvailablePythonCommand();
|
||||||
try {
|
try {
|
||||||
// Check if input file is a PDF
|
// Check if input file is a PDF
|
||||||
if ("pdf".equalsIgnoreCase(extension)) {
|
if ("pdf".equalsIgnoreCase(extension)) {
|
||||||
@@ -117,7 +123,7 @@ public class ExtractImageScansController {
|
|||||||
List<String> command =
|
List<String> command =
|
||||||
new ArrayList<>(
|
new ArrayList<>(
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
"python3",
|
pythonVersion,
|
||||||
"./scripts/split_photos.py",
|
"./scripts/split_photos.py",
|
||||||
images.get(i),
|
images.get(i),
|
||||||
tempDir.toString(),
|
tempDir.toString(),
|
||||||
|
|||||||
@@ -140,6 +140,9 @@ public class ExtractImagesController {
|
|||||||
Set<Integer> processedImages,
|
Set<Integer> processedImages,
|
||||||
ZipOutputStream zos)
|
ZipOutputStream zos)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
if (page.getResources() == null || page.getResources().getXObjectNames() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (COSName name : page.getResources().getXObjectNames()) {
|
for (COSName name : page.getResources().getXObjectNames()) {
|
||||||
if (page.getResources().isImageXObject(name)) {
|
if (page.getResources().isImageXObject(name)) {
|
||||||
PDImageXObject image = (PDImageXObject) page.getResources().getXObject(name);
|
PDImageXObject image = (PDImageXObject) page.getResources().getXObject(name);
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ import java.util.stream.Collectors;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
@@ -27,6 +26,7 @@ import io.github.pixee.security.Filenames;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
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.model.api.misc.ProcessPdfWithOcrRequest;
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||||
@@ -37,10 +37,10 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
|||||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||||
public class OCRController {
|
public class OCRController {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(OCRController.class);
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
public List<String> getAvailableTesseractLanguages() {
|
public List<String> getAvailableTesseractLanguages() {
|
||||||
String tessdataDir = "/usr/share/tessdata";
|
String tessdataDir = applicationProperties.getSystem().getTessdataDir();
|
||||||
File[] files = new File(tessdataDir).listFiles();
|
File[] files = new File(tessdataDir).listFiles();
|
||||||
if (files == null) {
|
if (files == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class ApiDocService {
|
|||||||
|
|
||||||
Map<String, List<String>> outputToFileTypes = new HashMap<>();
|
Map<String, List<String>> outputToFileTypes = new HashMap<>();
|
||||||
|
|
||||||
public List getExtensionTypes(boolean output, String operationName) {
|
public List<String> getExtensionTypes(boolean output, String operationName) {
|
||||||
if (outputToFileTypes.size() == 0) {
|
if (outputToFileTypes.size() == 0) {
|
||||||
outputToFileTypes.put("PDF", Arrays.asList("pdf"));
|
outputToFileTypes.put("PDF", Arrays.asList("pdf"));
|
||||||
outputToFileTypes.put(
|
outputToFileTypes.put(
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
package stirling.software.SPDF.controller.web;
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import java.util.Iterator;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import java.util.List;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import java.util.Map;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import java.util.Optional;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@@ -16,34 +13,35 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
|
|||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import stirling.software.SPDF.model.*;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
|
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.User;
|
|
||||||
import stirling.software.SPDF.model.provider.GithubProvider;
|
import stirling.software.SPDF.model.provider.GithubProvider;
|
||||||
import stirling.software.SPDF.model.provider.GoogleProvider;
|
import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||||
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
||||||
import stirling.software.SPDF.repository.UserRepository;
|
import stirling.software.SPDF.repository.UserRepository;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
|
@Slf4j
|
||||||
@Tag(name = "Account Security", description = "Account Security APIs")
|
@Tag(name = "Account Security", description = "Account Security APIs")
|
||||||
public class AccountWebController {
|
public class AccountWebController {
|
||||||
|
|
||||||
@Autowired ApplicationProperties applicationProperties;
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
private static final Logger logger = LoggerFactory.getLogger(AccountWebController.class);
|
@Autowired SessionPersistentRegistry sessionPersistentRegistry;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserRepository userRepository; // Assuming you have a repository for user operations
|
||||||
|
|
||||||
@GetMapping("/login")
|
@GetMapping("/login")
|
||||||
public String login(HttpServletRequest request, Model model, Authentication authentication) {
|
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()) {
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
return "redirect:/";
|
return "redirect:/";
|
||||||
}
|
}
|
||||||
@@ -137,6 +135,13 @@ public class AccountWebController {
|
|||||||
break;
|
break;
|
||||||
case "invalid_id_token":
|
case "invalid_id_token":
|
||||||
erroroauth = "login.oauth2InvalidIdToken";
|
erroroauth = "login.oauth2InvalidIdToken";
|
||||||
|
break;
|
||||||
|
case "oauth2_admin_blocked_user":
|
||||||
|
erroroauth = "login.oauth2AdminBlockedUser";
|
||||||
|
break;
|
||||||
|
case "userIsDisabled":
|
||||||
|
erroroauth = "login.userIsDisabled";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -155,9 +160,6 @@ public class AccountWebController {
|
|||||||
return "login";
|
return "login";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserRepository userRepository; // Assuming you have a repository for user operations
|
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@GetMapping("/addUsers")
|
@GetMapping("/addUsers")
|
||||||
public String showAddUserForm(
|
public String showAddUserForm(
|
||||||
@@ -166,6 +168,13 @@ public class AccountWebController {
|
|||||||
Iterator<User> iterator = allUsers.iterator();
|
Iterator<User> iterator = allUsers.iterator();
|
||||||
Map<String, String> roleDetails = Role.getAllRoleDetails();
|
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()) {
|
while (iterator.hasNext()) {
|
||||||
User user = iterator.next();
|
User user = iterator.next();
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
@@ -176,9 +185,73 @@ public class AccountWebController {
|
|||||||
break; // Break out of the inner loop once the user is removed
|
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 messageType = request.getParameter("messageType");
|
||||||
|
|
||||||
String deleteMessage = null;
|
String deleteMessage = null;
|
||||||
@@ -203,6 +276,9 @@ public class AccountWebController {
|
|||||||
case "invalidUsername":
|
case "invalidUsername":
|
||||||
addMessage = "invalidUsernameMessage";
|
addMessage = "invalidUsernameMessage";
|
||||||
break;
|
break;
|
||||||
|
case "invalidPassword":
|
||||||
|
addMessage = "invalidPasswordMessage";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -218,16 +294,24 @@ public class AccountWebController {
|
|||||||
case "downgradeCurrentUser":
|
case "downgradeCurrentUser":
|
||||||
changeMessage = "downgradeCurrentUserMessage";
|
changeMessage = "downgradeCurrentUserMessage";
|
||||||
break;
|
break;
|
||||||
|
case "disabledCurrentUser":
|
||||||
|
changeMessage = "disabledCurrentUserMessage";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
|
changeMessage = messageType;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
model.addAttribute("changeMessage", changeMessage);
|
model.addAttribute("changeMessage", changeMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
model.addAttribute("users", allUsers);
|
model.addAttribute("users", sortedUsers);
|
||||||
model.addAttribute("currentUsername", authentication.getName());
|
model.addAttribute("currentUsername", authentication.getName());
|
||||||
model.addAttribute("roleDetails", roleDetails);
|
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";
|
return "addUsers";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +349,7 @@ public class AccountWebController {
|
|||||||
if (username != null) {
|
if (username != null) {
|
||||||
// Fetch user details from the database
|
// Fetch user details from the database
|
||||||
Optional<User> user =
|
Optional<User> user =
|
||||||
userRepository.findByUsernameIgnoreCase(
|
userRepository.findByUsernameIgnoreCaseWithSettings(
|
||||||
username); // Assuming findByUsername method exists
|
username); // Assuming findByUsername method exists
|
||||||
if (!user.isPresent()) {
|
if (!user.isPresent()) {
|
||||||
return "redirect:/error";
|
return "redirect:/error";
|
||||||
@@ -278,7 +362,7 @@ public class AccountWebController {
|
|||||||
settingsJson = objectMapper.writeValueAsString(user.get().getSettings());
|
settingsJson = objectMapper.writeValueAsString(user.get().getSettings());
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
// Handle JSON conversion error
|
// Handle JSON conversion error
|
||||||
logger.error("exception", e);
|
log.error("exception", e);
|
||||||
return "redirect:/error";
|
return "redirect:/error";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import org.springframework.web.servlet.ModelAndView;
|
|||||||
import io.swagger.v3.oas.annotations.Hidden;
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.utils.CheckProgramInstall;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@Tag(name = "Convert", description = "Convert APIs")
|
@Tag(name = "Convert", description = "Convert APIs")
|
||||||
public class ConverterWebController {
|
public class ConverterWebController {
|
||||||
@@ -21,14 +23,6 @@ public class ConverterWebController {
|
|||||||
return "convert/book-to-pdf";
|
return "convert/book-to-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ConditionalOnExpression("#{bookAndHtmlFormatsInstalled}")
|
|
||||||
@GetMapping("/pdf-to-book")
|
|
||||||
@Hidden
|
|
||||||
public String convertPdfToBookForm(Model model) {
|
|
||||||
model.addAttribute("currentPage", "pdf-to-book");
|
|
||||||
return "convert/pdf-to-book";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/img-to-pdf")
|
@GetMapping("/img-to-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String convertImgToPdfForm(Model model) {
|
public String convertImgToPdfForm(Model model) {
|
||||||
@@ -57,13 +51,6 @@ public class ConverterWebController {
|
|||||||
return "convert/url-to-pdf";
|
return "convert/url-to-pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pdf-to-img")
|
|
||||||
@Hidden
|
|
||||||
public String pdfToimgForm(Model model) {
|
|
||||||
model.addAttribute("currentPage", "pdf-to-img");
|
|
||||||
return "convert/pdf-to-img";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/file-to-pdf")
|
@GetMapping("/file-to-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String convertToPdfForm(Model model) {
|
public String convertToPdfForm(Model model) {
|
||||||
@@ -73,6 +60,23 @@ public class ConverterWebController {
|
|||||||
|
|
||||||
// PDF TO......
|
// PDF TO......
|
||||||
|
|
||||||
|
@ConditionalOnExpression("#{bookAndHtmlFormatsInstalled}")
|
||||||
|
@GetMapping("/pdf-to-book")
|
||||||
|
@Hidden
|
||||||
|
public String convertPdfToBookForm(Model model) {
|
||||||
|
model.addAttribute("currentPage", "pdf-to-book");
|
||||||
|
return "convert/pdf-to-book";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/pdf-to-img")
|
||||||
|
@Hidden
|
||||||
|
public String pdfToimgForm(Model model) {
|
||||||
|
boolean isPython = CheckProgramInstall.isPythonAvailable();
|
||||||
|
model.addAttribute("isPython", isPython);
|
||||||
|
model.addAttribute("currentPage", "pdf-to-img");
|
||||||
|
return "convert/pdf-to-img";
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/pdf-to-html")
|
@GetMapping("/pdf-to-html")
|
||||||
@Hidden
|
@Hidden
|
||||||
public ModelAndView pdfToHTML() {
|
public ModelAndView pdfToHTML() {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -14,10 +15,15 @@ import org.springframework.web.servlet.ModelAndView;
|
|||||||
import io.swagger.v3.oas.annotations.Hidden;
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
import stirling.software.SPDF.utils.CheckProgramInstall;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
@Tag(name = "Misc", description = "Miscellaneous APIs")
|
||||||
public class OtherWebController {
|
public class OtherWebController {
|
||||||
|
|
||||||
|
@Autowired ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@GetMapping("/compress-pdf")
|
@GetMapping("/compress-pdf")
|
||||||
@Hidden
|
@Hidden
|
||||||
public String compressPdfForm(Model model) {
|
public String compressPdfForm(Model model) {
|
||||||
@@ -29,6 +35,8 @@ public class OtherWebController {
|
|||||||
@Hidden
|
@Hidden
|
||||||
public ModelAndView extractImageScansForm() {
|
public ModelAndView extractImageScansForm() {
|
||||||
ModelAndView modelAndView = new ModelAndView("misc/extract-image-scans");
|
ModelAndView modelAndView = new ModelAndView("misc/extract-image-scans");
|
||||||
|
boolean isPython = CheckProgramInstall.isPythonAvailable();
|
||||||
|
modelAndView.addObject("isPython", isPython);
|
||||||
modelAndView.addObject("currentPage", "extract-image-scans");
|
modelAndView.addObject("currentPage", "extract-image-scans");
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
@@ -97,7 +105,7 @@ public class OtherWebController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getAvailableTesseractLanguages() {
|
public List<String> getAvailableTesseractLanguages() {
|
||||||
String tessdataDir = "/usr/share/tessdata";
|
String tessdataDir = applicationProperties.getSystem().getTessdataDir();
|
||||||
File[] files = new File(tessdataDir).listFiles();
|
File[] files = new File(tessdataDir).listFiles();
|
||||||
if (files == null) {
|
if (files == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ public class ApplicationProperties {
|
|||||||
private String clientId;
|
private String clientId;
|
||||||
private String clientSecret;
|
private String clientSecret;
|
||||||
private Boolean autoCreateUser = false;
|
private Boolean autoCreateUser = false;
|
||||||
|
private Boolean blockRegistration = false;
|
||||||
private String useAsUsername;
|
private String useAsUsername;
|
||||||
private Collection<String> scopes = new ArrayList<>();
|
private Collection<String> scopes = new ArrayList<>();
|
||||||
private String provider;
|
private String provider;
|
||||||
@@ -286,6 +287,14 @@ public class ApplicationProperties {
|
|||||||
this.autoCreateUser = autoCreateUser;
|
this.autoCreateUser = autoCreateUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getBlockRegistration() {
|
||||||
|
return blockRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlockRegistration(Boolean blockRegistration) {
|
||||||
|
this.blockRegistration = blockRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
public String getUseAsUsername() {
|
public String getUseAsUsername() {
|
||||||
return useAsUsername;
|
return useAsUsername;
|
||||||
}
|
}
|
||||||
@@ -356,6 +365,8 @@ public class ApplicationProperties {
|
|||||||
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
|
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
|
||||||
+ ", autoCreateUser="
|
+ ", autoCreateUser="
|
||||||
+ autoCreateUser
|
+ autoCreateUser
|
||||||
|
+ ", blockRegistration="
|
||||||
|
+ blockRegistration
|
||||||
+ ", useAsUsername="
|
+ ", useAsUsername="
|
||||||
+ useAsUsername
|
+ useAsUsername
|
||||||
+ ", provider="
|
+ ", provider="
|
||||||
@@ -431,6 +442,15 @@ public class ApplicationProperties {
|
|||||||
private boolean showUpdate;
|
private boolean showUpdate;
|
||||||
private Boolean showUpdateOnlyAdmin;
|
private Boolean showUpdateOnlyAdmin;
|
||||||
private boolean customHTMLFiles;
|
private boolean customHTMLFiles;
|
||||||
|
private String tessdataDir;
|
||||||
|
|
||||||
|
public String getTessdataDir() {
|
||||||
|
return tessdataDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTessdataDir(String tessdataDir) {
|
||||||
|
this.tessdataDir = tessdataDir;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isCustomHTMLFiles() {
|
public boolean isCustomHTMLFiles() {
|
||||||
return customHTMLFiles;
|
return customHTMLFiles;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package stirling.software.SPDF.model;
|
package stirling.software.SPDF.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
@@ -11,7 +13,9 @@ import jakarta.persistence.Table;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "authorities")
|
@Table(name = "authorities")
|
||||||
public class Authority {
|
public class Authority implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public Authority() {}
|
public Authority() {}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -1,29 +1,19 @@
|
|||||||
package stirling.software.SPDF.model;
|
package stirling.software.SPDF.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import jakarta.persistence.CascadeType;
|
|
||||||
import jakarta.persistence.CollectionTable;
|
|
||||||
import jakarta.persistence.Column;
|
|
||||||
import jakarta.persistence.ElementCollection;
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.FetchType;
|
|
||||||
import jakarta.persistence.GeneratedValue;
|
|
||||||
import jakarta.persistence.GenerationType;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import jakarta.persistence.JoinColumn;
|
|
||||||
import jakarta.persistence.Lob;
|
|
||||||
import jakarta.persistence.MapKeyColumn;
|
|
||||||
import jakarta.persistence.OneToMany;
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "users")
|
@Table(name = "users")
|
||||||
public class User {
|
public class User implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public class ConvertToImageRequest extends PDFFile {
|
|||||||
|
|
||||||
@Schema(
|
@Schema(
|
||||||
description = "The output image format",
|
description = "The output image format",
|
||||||
allowableValues = {"png", "jpeg", "jpg", "gif"})
|
allowableValues = {"png", "jpeg", "jpg", "gif", "webp"})
|
||||||
private String imageFormat;
|
private String imageFormat;
|
||||||
|
|
||||||
@Schema(
|
@Schema(
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import lombok.EqualsAndHashCode;
|
|||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
public class UrlToPdfRequest {
|
public class UrlToPdfRequest {
|
||||||
|
|
||||||
@Schema(description = "The input URL to be converted to a PDF file", required = true)
|
@Schema(
|
||||||
|
description = "The input URL to be converted to a PDF file",
|
||||||
|
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String urlInput;
|
private String urlInput;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ import stirling.software.SPDF.model.api.PDFWithPageNums;
|
|||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class ContainsTextRequest extends PDFWithPageNums {
|
public class ContainsTextRequest extends PDFWithPageNums {
|
||||||
|
|
||||||
@Schema(description = "The text to check for", required = true)
|
@Schema(description = "The text to check for", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String text;
|
private String text;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ import stirling.software.SPDF.model.api.PDFComparison;
|
|||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class FileSizeRequest extends PDFComparison {
|
public class FileSizeRequest extends PDFComparison {
|
||||||
|
|
||||||
@Schema(description = "File Size", required = true)
|
@Schema(description = "File Size", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String fileSize;
|
private String fileSize;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ import stirling.software.SPDF.model.api.PDFComparison;
|
|||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class PageRotationRequest extends PDFComparison {
|
public class PageRotationRequest extends PDFComparison {
|
||||||
|
|
||||||
@Schema(description = "Rotation in degrees", required = true)
|
@Schema(description = "Rotation in degrees", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private int rotation;
|
private int rotation;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ import stirling.software.SPDF.model.api.PDFComparison;
|
|||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class PageSizeRequest extends PDFComparison {
|
public class PageSizeRequest extends PDFComparison {
|
||||||
|
|
||||||
@Schema(description = "Standard Page Size", required = true)
|
@Schema(description = "Standard Page Size", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String standardPageSize;
|
private String standardPageSize;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ public class OverlayPdfsRequest extends PDFFile {
|
|||||||
@Schema(
|
@Schema(
|
||||||
description =
|
description =
|
||||||
"The mode of overlaying: 'SequentialOverlay' for sequential application, 'InterleavedOverlay' for round-robin application, 'FixedRepeatOverlay' for fixed repetition based on provided counts",
|
"The mode of overlaying: 'SequentialOverlay' for sequential application, 'InterleavedOverlay' for round-robin application, 'FixedRepeatOverlay' for fixed repetition based on provided counts",
|
||||||
required = true)
|
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String overlayMode;
|
private String overlayMode;
|
||||||
|
|
||||||
@Schema(
|
@Schema(
|
||||||
description =
|
description =
|
||||||
"An array of integers specifying the number of times each corresponding overlay file should be applied in the 'FixedRepeatOverlay' mode. This should match the length of the overlayFiles array.",
|
"An array of integers specifying the number of times each corresponding overlay file should be applied in the 'FixedRepeatOverlay' mode. This should match the length of the overlayFiles array.",
|
||||||
required = false)
|
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||||
private int[] counts;
|
private int[] counts;
|
||||||
|
|
||||||
@Schema(description = "Overlay position 0 is Foregound, 1 is Background")
|
@Schema(description = "Overlay position 0 is Foregound, 1 is Background")
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ public class SplitPdfBySizeOrCountRequest extends PDFFile {
|
|||||||
@Schema(
|
@Schema(
|
||||||
description =
|
description =
|
||||||
"Determines the type of split: 0 for size, 1 for page count, 2 for document count",
|
"Determines the type of split: 0 for size, 1 for page count, 2 for document count",
|
||||||
required = false,
|
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
|
||||||
defaultValue = "0")
|
defaultValue = "0")
|
||||||
private int splitType;
|
private int splitType;
|
||||||
|
|
||||||
@Schema(
|
@Schema(
|
||||||
description =
|
description =
|
||||||
"Value for split: size in MB (e.g., '10MB') or number of pages (e.g., '5')",
|
"Value for split: size in MB (e.g., '10MB') or number of pages (e.g., '5')",
|
||||||
required = false,
|
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
|
||||||
defaultValue = "10MB")
|
defaultValue = "10MB")
|
||||||
private String splitValue;
|
private String splitValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class AddStampRequest extends PDFWithPageNums {
|
|||||||
@Schema(
|
@Schema(
|
||||||
description = "The stamp type (text or image)",
|
description = "The stamp type (text or image)",
|
||||||
allowableValues = {"text", "image"},
|
allowableValues = {"text", "image"},
|
||||||
required = true)
|
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String stampType;
|
private String stampType;
|
||||||
|
|
||||||
@Schema(description = "The stamp text")
|
@Schema(description = "The stamp text")
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class AutoSplitPdfRequest extends PDFFile {
|
|||||||
@Schema(
|
@Schema(
|
||||||
description =
|
description =
|
||||||
"Flag indicating if the duplex mode is active, where the page after the divider also gets removed.",
|
"Flag indicating if the duplex mode is active, where the page after the divider also gets removed.",
|
||||||
required = false,
|
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
|
||||||
defaultValue = "false")
|
defaultValue = "false")
|
||||||
private boolean duplexMode;
|
private boolean duplexMode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class ExtractHeaderRequest extends PDFFile {
|
|||||||
@Schema(
|
@Schema(
|
||||||
description =
|
description =
|
||||||
"Flag indicating whether to use the first text as a fallback if no suitable title is found. Defaults to false.",
|
"Flag indicating whether to use the first text as a fallback if no suitable title is found. Defaults to false.",
|
||||||
required = false,
|
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
|
||||||
defaultValue = "false")
|
defaultValue = "false")
|
||||||
private boolean useFirstTextAsFallback;
|
private boolean useFirstTextAsFallback;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import lombok.EqualsAndHashCode;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
public class ExtractImageScansRequest {
|
public class ExtractImageScansRequest {
|
||||||
@Schema(description = "The input file containing image scans", required = true)
|
@Schema(
|
||||||
|
description = "The input file containing image scans",
|
||||||
|
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private MultipartFile fileInput;
|
private MultipartFile fileInput;
|
||||||
|
|
||||||
@Schema(
|
@Schema(
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import stirling.software.SPDF.model.api.PDFFile;
|
|||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class PrintFileRequest extends PDFFile {
|
public class PrintFileRequest extends PDFFile {
|
||||||
|
|
||||||
@Schema(description = "Name of printer to match against", required = true)
|
@Schema(
|
||||||
|
description = "Name of printer to match against",
|
||||||
|
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String printerName;
|
private String printerName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class AddWatermarkRequest extends PDFFile {
|
|||||||
@Schema(
|
@Schema(
|
||||||
description = "The watermark type (text or image)",
|
description = "The watermark type (text or image)",
|
||||||
allowableValues = {"text", "image"},
|
allowableValues = {"text", "image"},
|
||||||
required = true)
|
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String watermarkType;
|
private String watermarkType;
|
||||||
|
|
||||||
@Schema(description = "The watermark text")
|
@Schema(description = "The watermark text")
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import stirling.software.SPDF.model.api.PDFFile;
|
|||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class PDFPasswordRequest extends PDFFile {
|
public class PDFPasswordRequest extends PDFFile {
|
||||||
|
|
||||||
@Schema(description = "The password of the PDF file", required = true)
|
@Schema(
|
||||||
|
description = "The password of the PDF file",
|
||||||
|
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String password;
|
private String password;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ import stirling.software.SPDF.model.api.PDFFile;
|
|||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class RedactPdfRequest extends PDFFile {
|
public class RedactPdfRequest extends PDFFile {
|
||||||
|
|
||||||
@Schema(description = "List of text to redact from the PDF", type = "string", required = true)
|
@Schema(
|
||||||
|
description = "List of text to redact from the PDF",
|
||||||
|
type = "string",
|
||||||
|
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String listOfText;
|
private String listOfText;
|
||||||
|
|
||||||
@Schema(description = "Whether to use regex for the listOfText", defaultValue = "false")
|
@Schema(description = "Whether to use regex for the listOfText", defaultValue = "false")
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ public class GoogleProvider extends Provider {
|
|||||||
private Collection<String> scopes = new ArrayList<>();
|
private Collection<String> scopes = new ArrayList<>();
|
||||||
private String useAsUsername = "email";
|
private String useAsUsername = "email";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIssuer() {
|
||||||
|
return new String();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setIssuer(String issuer) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getClientId() {
|
public String getClientId() {
|
||||||
return this.clientId;
|
return this.clientId;
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
package stirling.software.SPDF.repository;
|
package stirling.software.SPDF.repository;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface UserRepository extends JpaRepository<User, Long> {
|
public interface UserRepository extends JpaRepository<User, Long> {
|
||||||
Optional<User> findByUsernameIgnoreCase(String username);
|
Optional<User> findByUsernameIgnoreCase(String username);
|
||||||
|
|
||||||
|
@Query("FROM User u LEFT JOIN FETCH u.settings where upper(u.username) = upper(:username)")
|
||||||
|
Optional<User> findByUsernameIgnoreCaseWithSettings(String username);
|
||||||
|
|
||||||
Optional<User> findByUsername(String username);
|
Optional<User> findByUsername(String username);
|
||||||
|
|
||||||
User findByApiKey(String apiKey);
|
Optional<User> findByApiKey(String apiKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package stirling.software.SPDF.service;
|
package stirling.software.SPDF.service;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.pdfbox.cos.COSName;
|
import org.apache.pdfbox.cos.COSName;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
@@ -16,7 +18,7 @@ public class PdfImageRemovalService {
|
|||||||
/**
|
/**
|
||||||
* Removes all image objects from the provided PDF document.
|
* Removes all image objects from the provided PDF document.
|
||||||
*
|
*
|
||||||
* This method iterates over each page in the document and removes any image XObjects found
|
* <p>This method iterates over each page in the document and removes any image XObjects found
|
||||||
* in the page's resources.
|
* in the page's resources.
|
||||||
*
|
*
|
||||||
* @param document The PDF document from which images will be removed.
|
* @param document The PDF document from which images will be removed.
|
||||||
@@ -27,14 +29,22 @@ public class PdfImageRemovalService {
|
|||||||
// Iterate over each page in the PDF document
|
// Iterate over each page in the PDF document
|
||||||
for (PDPage page : document.getPages()) {
|
for (PDPage page : document.getPages()) {
|
||||||
PDResources resources = page.getResources();
|
PDResources resources = page.getResources();
|
||||||
|
// Collect the XObject names to remove
|
||||||
|
List<COSName> namesToRemove = new ArrayList<>();
|
||||||
|
|
||||||
// Iterate over all XObject names in the page's resources
|
// Iterate over all XObject names in the page's resources
|
||||||
for (COSName name : resources.getXObjectNames()) {
|
for (COSName name : resources.getXObjectNames()) {
|
||||||
// Check if the XObject is an image
|
// Check if the XObject is an image
|
||||||
if (resources.isImageXObject(name)) {
|
if (resources.isImageXObject(name)) {
|
||||||
// Remove the image XObject by setting it to null
|
// Collect the name for removal
|
||||||
resources.put(name, (PDXObject) null);
|
namesToRemove.add(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now, modify the resources by removing the collected names
|
||||||
|
for (COSName name : namesToRemove) {
|
||||||
|
resources.put(name, (PDXObject) null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package stirling.software.SPDF.utils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||||
|
|
||||||
|
public class CheckProgramInstall {
|
||||||
|
|
||||||
|
private static final List<String> PYTHON_COMMANDS = Arrays.asList("python3", "python");
|
||||||
|
private static boolean pythonAvailableChecked = false;
|
||||||
|
private static String availablePythonCommand = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks which Python command is available and returns it.
|
||||||
|
*
|
||||||
|
* @return The available Python command ("python3" or "python"), or null if neither is
|
||||||
|
* available.
|
||||||
|
*/
|
||||||
|
public static String getAvailablePythonCommand() {
|
||||||
|
if (!pythonAvailableChecked) {
|
||||||
|
availablePythonCommand =
|
||||||
|
PYTHON_COMMANDS.stream()
|
||||||
|
.filter(CheckProgramInstall::checkPythonVersion)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
pythonAvailableChecked = true;
|
||||||
|
}
|
||||||
|
return availablePythonCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the specified command is available by running the command with --version.
|
||||||
|
*
|
||||||
|
* @param pythonCommand The Python command to check.
|
||||||
|
* @return true if the command is available, false otherwise.
|
||||||
|
*/
|
||||||
|
private static boolean checkPythonVersion(String pythonCommand) {
|
||||||
|
try {
|
||||||
|
ProcessExecutorResult result =
|
||||||
|
ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV)
|
||||||
|
.runCommandWithOutputHandling(
|
||||||
|
Arrays.asList(pythonCommand, "--version"));
|
||||||
|
return true; // Command succeeded, Python is available
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
return false; // Command failed, Python is not available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if any Python command is available.
|
||||||
|
*
|
||||||
|
* @return true if any Python command is available, false otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean isPythonAvailable() {
|
||||||
|
return getAvailablePythonCommand() != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,10 @@ import java.io.File;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
import java.nio.file.FileVisitResult;
|
import java.nio.file.FileVisitResult;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -13,8 +16,6 @@ import java.nio.file.SimpleFileVisitor;
|
|||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.net.URL;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -73,12 +74,11 @@ public class GeneralUtils {
|
|||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isURLReachable(String urlStr) {
|
public static boolean isURLReachable(String urlStr) {
|
||||||
try {
|
try {
|
||||||
URL url = new URL(urlStr);
|
URL url = URI.create(urlStr).toURL();
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
connection.setRequestMethod("HEAD");
|
connection.setRequestMethod("HEAD");
|
||||||
int responseCode = connection.getResponseCode();
|
int responseCode = connection.getResponseCode();
|
||||||
@@ -112,16 +112,19 @@ public class GeneralUtils {
|
|||||||
sizeStr = sizeStr.replace(",", ".").replace(" ", "");
|
sizeStr = sizeStr.replace(",", ".").replace(" ", "");
|
||||||
try {
|
try {
|
||||||
if (sizeStr.endsWith("KB")) {
|
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")) {
|
} else if (sizeStr.endsWith("MB")) {
|
||||||
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
|
return (long)
|
||||||
* 1024
|
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
|
||||||
* 1024);
|
* 1024
|
||||||
|
* 1024);
|
||||||
} else if (sizeStr.endsWith("GB")) {
|
} else if (sizeStr.endsWith("GB")) {
|
||||||
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
|
return (long)
|
||||||
* 1024
|
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
|
||||||
* 1024
|
* 1024
|
||||||
* 1024);
|
* 1024
|
||||||
|
* 1024);
|
||||||
} else if (sizeStr.endsWith("B")) {
|
} else if (sizeStr.endsWith("B")) {
|
||||||
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
|
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
|
||||||
} else {
|
} else {
|
||||||
@@ -191,8 +194,7 @@ public class GeneralUtils {
|
|||||||
|
|
||||||
// Check if the result is null or not within bounds
|
// Check if the result is null or not within bounds
|
||||||
if (result == null || result <= 0 || result.intValue() > maxValue) {
|
if (result == null || result <= 0 || result.intValue() > maxValue) {
|
||||||
if (n != 0)
|
if (n != 0) break;
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
results.add(result.intValue());
|
results.add(result.intValue());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
multipart.enabled=true
|
multipart.enabled=true
|
||||||
|
|
||||||
|
logging.level.org.springframework=WARN
|
||||||
|
logging.level.org.hibernate=WARN
|
||||||
|
logging.level.org.eclipse.jetty=WARN
|
||||||
|
logging.level.com.zaxxer.hikari=WARN
|
||||||
|
|
||||||
|
spring.jpa.open-in-view=false
|
||||||
|
|
||||||
server.forward-headers-strategy=NATIVE
|
server.forward-headers-strategy=NATIVE
|
||||||
|
|
||||||
@@ -24,10 +30,8 @@ spring.devtools.livereload.enabled=true
|
|||||||
|
|
||||||
spring.thymeleaf.encoding=UTF-8
|
spring.thymeleaf.encoding=UTF-8
|
||||||
|
|
||||||
server.connection-timeout=${SYSTEM_CONNECTIONTIMEOUTMINUTES:20m}
|
|
||||||
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
|
|
||||||
|
|
||||||
spring.resources.static-locations=file:customFiles/static/
|
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
|
||||||
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
|
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
|
||||||
#spring.thymeleaf.cache=false
|
#spring.thymeleaf.cache=false
|
||||||
|
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=User not found.
|
|||||||
incorrectPasswordMessage=Current password is incorrect.
|
incorrectPasswordMessage=Current password is incorrect.
|
||||||
usernameExistsMessage=New Username already exists.
|
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.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=لا يمكن خفض دور المستخدم الحالي
|
downgradeCurrentUserMessage=لا يمكن خفض دور المستخدم الحالي
|
||||||
|
disabledCurrentUserMessage=The current user cannot be disabled
|
||||||
downgradeCurrentUserLongMessage=لا يمكن تخفيض دور المستخدم الحالي. وبالتالي، لن يظهر المستخدم الحالي.
|
downgradeCurrentUserLongMessage=لا يمكن تخفيض دور المستخدم الحالي. وبالتالي، لن يظهر المستخدم الحالي.
|
||||||
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=User
|
|||||||
adminUserSettings.addUser=Add New User
|
adminUserSettings.addUser=Add New User
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||||
adminUserSettings.roles=Roles
|
adminUserSettings.roles=Roles
|
||||||
adminUserSettings.role=Role
|
adminUserSettings.role=Role
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Force user to change password on login
|
|||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=تغيير دور المستخدم
|
adminUserSettings.changeUserRole=تغيير دور المستخدم
|
||||||
adminUserSettings.authenticated=Authenticated
|
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
|
database.title=Database Import/Export
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=squish,small,tiny
|
|||||||
|
|
||||||
home.changeMetadata.title=تغيير البيانات الوصفية
|
home.changeMetadata.title=تغيير البيانات الوصفية
|
||||||
home.changeMetadata.desc=تغيير / إزالة / إضافة بيانات أولية من مستند PDF
|
home.changeMetadata.desc=تغيير / إزالة / إضافة بيانات أولية من مستند PDF
|
||||||
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=تحويل الملف إلى PDF
|
home.fileToPDF.title=تحويل الملف إلى PDF
|
||||||
home.fileToPDF.desc=تحويل أي ملف تقريبا إلى PDF (DOCX وPNG وXLS وPPT وTXT والمزيد)
|
home.fileToPDF.desc=تحويل أي ملف تقريبا إلى PDF (DOCX وPNG وXLS وPPT وTXT والمزيد)
|
||||||
@@ -481,12 +491,14 @@ login.locked=Your account has been locked.
|
|||||||
login.signinTitle=Please sign in
|
login.signinTitle=Please sign in
|
||||||
login.ssoSignIn=تسجيل الدخول عبر تسجيل الدخول الأحادي
|
login.ssoSignIn=تسجيل الدخول عبر تسجيل الدخول الأحادي
|
||||||
login.oauth2AutoCreateDisabled=تم تعطيل مستخدم الإنشاء التلقائي لـ OAuth2
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=الحد الأدنى لمنطقة المحيط:
|
|||||||
ScannerImageSplit.selectText.8=تعيين الحد الأدنى لمنطقة المحيط للصورة
|
ScannerImageSplit.selectText.8=تعيين الحد الأدنى لمنطقة المحيط للصورة
|
||||||
ScannerImageSplit.selectText.9=حجم الحدود:
|
ScannerImageSplit.selectText.9=حجم الحدود:
|
||||||
ScannerImageSplit.selectText.10=يضبط حجم الحدود المضافة والمزالة لمنع الحدود البيضاء في الإخراج (الافتراضي: 1).
|
ScannerImageSplit.selectText.10=يضبط حجم الحدود المضافة والمزالة لمنع الحدود البيضاء في الإخراج (الافتراضي: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=اللون
|
|||||||
pdfToImage.grey=تدرج الرمادي
|
pdfToImage.grey=تدرج الرمادي
|
||||||
pdfToImage.blackwhite=أبيض وأسود (قد يفقد البيانات!)
|
pdfToImage.blackwhite=أبيض وأسود (قد يفقد البيانات!)
|
||||||
pdfToImage.submit=تحول
|
pdfToImage.submit=تحول
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Потребителят не е намерен
|
|||||||
incorrectPasswordMessage=Текущата парола е неправилна.
|
incorrectPasswordMessage=Текущата парола е неправилна.
|
||||||
usernameExistsMessage=Новият потребител вече съществува.
|
usernameExistsMessage=Новият потребител вече съществува.
|
||||||
invalidUsernameMessage=Невалидно потребителско име, потребителското име може да съдържа само букви, цифри и следните специални знаци @._+- или трябва да е валиден имейл адрес.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Не може да се изтрие вписания в момента потребител.
|
deleteCurrentUserMessage=Не може да се изтрие вписания в момента потребител.
|
||||||
deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито.
|
deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито.
|
||||||
downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител
|
downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител
|
||||||
|
disabledCurrentUserMessage=The current user cannot be disabled
|
||||||
downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан.
|
downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан.
|
||||||
userAlreadyExistsOAuthMessage=Потребителят вече съществува като OAuth2 потребител.
|
userAlreadyExistsOAuthMessage=Потребителят вече съществува като OAuth2 потребител.
|
||||||
userAlreadyExistsWebMessage=Потребителят вече съществува като уеб-потребител.
|
userAlreadyExistsWebMessage=Потребителят вече съществува като уеб-потребител.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Потребител
|
|||||||
adminUserSettings.addUser=Добавяне на нов потребител
|
adminUserSettings.addUser=Добавяне на нов потребител
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
||||||
|
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||||
adminUserSettings.usernameInfo=Потребителското име може да съдържа само букви, цифри и следните специални символи @._+- или трябва да е валиден имейл адрес.
|
adminUserSettings.usernameInfo=Потребителското име може да съдържа само букви, цифри и следните специални символи @._+- или трябва да е валиден имейл адрес.
|
||||||
adminUserSettings.roles=Роли
|
adminUserSettings.roles=Роли
|
||||||
adminUserSettings.role=Роля
|
adminUserSettings.role=Роля
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Принудете потребителя да п
|
|||||||
adminUserSettings.submit=Съхранете потребителя
|
adminUserSettings.submit=Съхранете потребителя
|
||||||
adminUserSettings.changeUserRole=Промяна на ролята на потребителя
|
adminUserSettings.changeUserRole=Промяна на ролята на потребителя
|
||||||
adminUserSettings.authenticated=Удостоверен
|
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
|
database.title=Database Import/Export
|
||||||
@@ -481,12 +491,14 @@ login.locked=Вашият акаунт е заключен.
|
|||||||
login.signinTitle=Моля впишете се
|
login.signinTitle=Моля впишете се
|
||||||
login.ssoSignIn=Влизане чрез еднократно влизане
|
login.ssoSignIn=Влизане чрез еднократно влизане
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано
|
login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано
|
||||||
|
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
|
||||||
login.oauth2RequestNotFound=Заявката за оторизация не е намерена
|
login.oauth2RequestNotFound=Заявката за оторизация не е намерена
|
||||||
login.oauth2InvalidUserInfoResponse=Невалидна информация за потребителя
|
login.oauth2InvalidUserInfoResponse=Невалидна информация за потребителя
|
||||||
login.oauth2invalidRequest=Невалидна заявка
|
login.oauth2invalidRequest=Невалидна заявка
|
||||||
login.oauth2AccessDenied=Отказан достъп
|
login.oauth2AccessDenied=Отказан достъп
|
||||||
login.oauth2InvalidTokenResponse=Невалиден отговор на токена
|
login.oauth2InvalidTokenResponse=Невалиден отговор на токена
|
||||||
login.oauth2InvalidIdToken=Невалиден токен за идентификатор
|
login.oauth2InvalidIdToken=Невалиден токен за идентификатор
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Минимална контурна площ:
|
|||||||
ScannerImageSplit.selectText.8=Задава минималния праг на контурната площ за изображение
|
ScannerImageSplit.selectText.8=Задава минималния праг на контурната площ за изображение
|
||||||
ScannerImageSplit.selectText.9=Размер на рамката:
|
ScannerImageSplit.selectText.9=Размер на рамката:
|
||||||
ScannerImageSplit.selectText.10=Задава размера на добавената и премахната граница, за да предотврати бели граници към изхода (по подразбиране: 1).
|
ScannerImageSplit.selectText.10=Задава размера на добавената и премахната граница, за да предотврати бели граници към изхода (по подразбиране: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Цвят
|
|||||||
pdfToImage.grey=Скала на сивото
|
pdfToImage.grey=Скала на сивото
|
||||||
pdfToImage.blackwhite=Черно и бяло (може да загубите данни!)
|
pdfToImage.blackwhite=Черно и бяло (може да загубите данни!)
|
||||||
pdfToImage.submit=Преобразуване
|
pdfToImage.submit=Преобразуване
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=User not found.
|
|||||||
incorrectPasswordMessage=Current password is incorrect.
|
incorrectPasswordMessage=Current password is incorrect.
|
||||||
usernameExistsMessage=New Username already exists.
|
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.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=No es pot reduir la funció de l'usuari actual
|
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.
|
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.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Usuari
|
|||||||
adminUserSettings.addUser=Afegir Usuari
|
adminUserSettings.addUser=Afegir Usuari
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||||
adminUserSettings.roles=Rols
|
adminUserSettings.roles=Rols
|
||||||
adminUserSettings.role=Rol
|
adminUserSettings.role=Rol
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Force user to change password on login
|
|||||||
adminUserSettings.submit=Desar Usuari
|
adminUserSettings.submit=Desar Usuari
|
||||||
adminUserSettings.changeUserRole=Canvia el rol de l'usuari
|
adminUserSettings.changeUserRole=Canvia el rol de l'usuari
|
||||||
adminUserSettings.authenticated=Authenticated
|
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
|
database.title=Database Import/Export
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=squish,small,tiny
|
|||||||
|
|
||||||
home.changeMetadata.title=Canvia Metadades
|
home.changeMetadata.title=Canvia Metadades
|
||||||
home.changeMetadata.desc=Canvia/Treu/Afegeix matadades al document PDF.
|
home.changeMetadata.desc=Canvia/Treu/Afegeix matadades al document PDF.
|
||||||
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=Converteix arxiu a PDF
|
home.fileToPDF.title=Converteix arxiu a PDF
|
||||||
home.fileToPDF.desc=Converteix qualsevol arxiu a PDF (DOCX, PNG, XLS, PPT, TXT i més)
|
home.fileToPDF.desc=Converteix qualsevol arxiu a PDF (DOCX, PNG, XLS, PPT, TXT i més)
|
||||||
@@ -481,12 +491,14 @@ login.locked=Compte bloquejat
|
|||||||
login.signinTitle=Autenticat
|
login.signinTitle=Autenticat
|
||||||
login.ssoSignIn=Inicia sessió mitjançant l'inici de sessió ún
|
login.ssoSignIn=Inicia sessió mitjançant l'inici de sessió ún
|
||||||
login.oauth2AutoCreateDisabled=L'usuari de creació automàtica OAUTH2 està desactivat
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Àrea de contorn mínima:
|
|||||||
ScannerImageSplit.selectText.8=Estableix el llindar mínim de l'àrea de contorn per a una foto
|
ScannerImageSplit.selectText.8=Estableix el llindar mínim de l'àrea de contorn per a una foto
|
||||||
ScannerImageSplit.selectText.9=Mida Vora:
|
ScannerImageSplit.selectText.9=Mida Vora:
|
||||||
ScannerImageSplit.selectText.10=Estableix la mida de la vora afegida i eliminada per evitar vores blanques a la sortida (per defecte: 1).
|
ScannerImageSplit.selectText.10=Estableix la mida de la vora afegida i eliminada per evitar vores blanques a la sortida (per defecte: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Color
|
|||||||
pdfToImage.grey=Escala de Grisos
|
pdfToImage.grey=Escala de Grisos
|
||||||
pdfToImage.blackwhite=Blanc i Negre (Pot perdre dades!)
|
pdfToImage.blackwhite=Blanc i Negre (Pot perdre dades!)
|
||||||
pdfToImage.submit=Converteix
|
pdfToImage.submit=Converteix
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Uživatel nenalezen.
|
|||||||
incorrectPasswordMessage=Současné heslo není správné.
|
incorrectPasswordMessage=Současné heslo není správné.
|
||||||
usernameExistsMessage=Nové uživatelské jméno již existuje.
|
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.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Nelze smazat aktuální přihlášeného uživatele.
|
deleteCurrentUserMessage=Nelze smazat aktuální přihlášeného uživatele.
|
||||||
deleteUsernameExistsMessage=Uživatelské jméno neexistuje a nelze ho smazat.
|
deleteUsernameExistsMessage=Uživatelské jméno neexistuje a nelze ho smazat.
|
||||||
downgradeCurrentUserMessage=Nelze snížit roli aktuálního uživatele.
|
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.
|
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.
|
userAlreadyExistsOAuthMessage=Uživatel již existuje jako OAuth2 uživatel.
|
||||||
userAlreadyExistsWebMessage=Uživatel již existuje jako webový uživatel.
|
userAlreadyExistsWebMessage=Uživatel již existuje jako webový uživatel.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Uživatel
|
|||||||
adminUserSettings.addUser=Přidat Nového Uživatele
|
adminUserSettings.addUser=Přidat Nového Uživatele
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.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.roles=Role
|
||||||
adminUserSettings.role=Role
|
adminUserSettings.role=Role
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Vynutit uživateli změnu hesla při přihlášen
|
|||||||
adminUserSettings.submit=Uložit Uživatele
|
adminUserSettings.submit=Uložit Uživatele
|
||||||
adminUserSettings.changeUserRole=Zmenit Roli Uživatele
|
adminUserSettings.changeUserRole=Zmenit Roli Uživatele
|
||||||
adminUserSettings.authenticated=Ověřeno
|
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
|
database.title=Database Import/Export
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=rozmáčknout,malý,drobný
|
|||||||
|
|
||||||
home.changeMetadata.title=Změnit Metadata
|
home.changeMetadata.title=Změnit Metadata
|
||||||
home.changeMetadata.desc=Změnit/Odebrat/Přidat metadata z PDF dokumentu
|
home.changeMetadata.desc=Změnit/Odebrat/Přidat metadata z PDF dokumentu
|
||||||
changeMetadata.tags==Název,autor,datum,tvorba,čas,vydavatel,výrobce,statistiky
|
changeMetadata.tags=Název,autor,datum,tvorba,čas,vydavatel,výrobce,statistiky
|
||||||
|
|
||||||
home.fileToPDF.title=Převést soubor do PDF
|
home.fileToPDF.title=Převést soubor do PDF
|
||||||
home.fileToPDF.desc=Převést téměř jakýkoli soubor do PDF (DOCX, PNG, XLS, PPT, TXT a více)
|
home.fileToPDF.desc=Převést téměř jakýkoli soubor do PDF (DOCX, PNG, XLS, PPT, TXT a více)
|
||||||
@@ -481,12 +491,14 @@ login.locked=Your account has been locked.
|
|||||||
login.signinTitle=Please sign in
|
login.signinTitle=Please sign in
|
||||||
login.ssoSignIn=Login via Single Sign-on
|
login.ssoSignIn=Login via Single Sign-on
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimální plocha kontury:
|
|||||||
ScannerImageSplit.selectText.8=Nastaví minimální plošný práh kontury pro fotografii
|
ScannerImageSplit.selectText.8=Nastaví minimální plošný práh kontury pro fotografii
|
||||||
ScannerImageSplit.selectText.9=Velikost okraje:
|
ScannerImageSplit.selectText.9=Velikost okraje:
|
||||||
ScannerImageSplit.selectText.10=Nastaví velikost okraje přidaného a odebraného k zabránění bílých ohraničení ve výstupu (výchozí: 1).
|
ScannerImageSplit.selectText.10=Nastaví velikost okraje přidaného a odebraného k zabránění bílých ohraničení ve výstupu (výchozí: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Barevný
|
|||||||
pdfToImage.grey=Stupně šedi
|
pdfToImage.grey=Stupně šedi
|
||||||
pdfToImage.blackwhite=Černobílý (Může dojít k ztrátě dat!)
|
pdfToImage.blackwhite=Černobílý (Může dojít k ztrátě dat!)
|
||||||
pdfToImage.submit=Převést
|
pdfToImage.submit=Převést
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
###########
|
###########
|
||||||
# Generic #
|
# Generic #
|
||||||
###########
|
###########
|
||||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||||
language.direction=ltr
|
language.direction=ltr
|
||||||
|
|
||||||
pdfPrompt=Vælg PDF-fil(er)
|
pdfPrompt=Vælg PDF-fil(er)
|
||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Bruger ikke fundet.
|
|||||||
incorrectPasswordMessage=Nuværende adgangskode er forkert.
|
incorrectPasswordMessage=Nuværende adgangskode er forkert.
|
||||||
usernameExistsMessage=Nyt brugernavn findes allerede.
|
usernameExistsMessage=Nyt brugernavn findes allerede.
|
||||||
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Cannot downgrade current user's role
|
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.
|
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
|
||||||
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=User
|
|||||||
adminUserSettings.addUser=Add New User
|
adminUserSettings.addUser=Add New User
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||||
adminUserSettings.roles=Roles
|
adminUserSettings.roles=Roles
|
||||||
adminUserSettings.role=Role
|
adminUserSettings.role=Role
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Force user to change password on login
|
|||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=Change User's Role
|
adminUserSettings.changeUserRole=Change User's Role
|
||||||
adminUserSettings.authenticated=Authenticated
|
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
|
database.title=Database Import/Export
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=squish,small,tiny
|
|||||||
|
|
||||||
home.changeMetadata.title=Change Metadata
|
home.changeMetadata.title=Change Metadata
|
||||||
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
|
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
|
||||||
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=Convert file to PDF
|
home.fileToPDF.title=Convert file to PDF
|
||||||
home.fileToPDF.desc=Convert nearly any file to PDF (DOCX, PNG, XLS, PPT, TXT and more)
|
home.fileToPDF.desc=Convert nearly any file to PDF (DOCX, PNG, XLS, PPT, TXT and more)
|
||||||
@@ -481,12 +491,14 @@ login.locked=Your account has been locked.
|
|||||||
login.signinTitle=Please sign in
|
login.signinTitle=Please sign in
|
||||||
login.ssoSignIn=Login via Single Sign-on
|
login.ssoSignIn=Login via Single Sign-on
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimum Contour Area:
|
|||||||
ScannerImageSplit.selectText.8=Sets the minimum contour area threshold for a photo
|
ScannerImageSplit.selectText.8=Sets the minimum contour area threshold for a photo
|
||||||
ScannerImageSplit.selectText.9=Border Size:
|
ScannerImageSplit.selectText.9=Border Size:
|
||||||
ScannerImageSplit.selectText.10=Sets the size of the border added and removed to prevent white borders in the output (default: 1).
|
ScannerImageSplit.selectText.10=Sets the size of the border added and removed to prevent white borders in the output (default: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Colour
|
|||||||
pdfToImage.grey=Greyscale
|
pdfToImage.grey=Greyscale
|
||||||
pdfToImage.blackwhite=Black and White (May lose data!)
|
pdfToImage.blackwhite=Black and White (May lose data!)
|
||||||
pdfToImage.submit=Convert
|
pdfToImage.submit=Convert
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||||
language.direction=ltr
|
language.direction=ltr
|
||||||
|
|
||||||
pdfPrompt=PDF auswählen
|
pdfPrompwt=PDF auswählen
|
||||||
multiPdfPrompt=PDFs auswählen(2+)
|
multiPdfPrompt=PDFs auswählen(2+)
|
||||||
multiPdfDropPrompt=Wählen Sie alle gewünschten PDFs aus (oder ziehen Sie sie per Drag & Drop hierhin)
|
multiPdfDropPrompt=Wählen Sie alle gewünschten PDFs aus (oder ziehen Sie sie per Drag & Drop hierhin)
|
||||||
imgPrompt=Wählen Sie ein Bild
|
imgPrompt=Wählen Sie ein Bild
|
||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Benutzer nicht gefunden.
|
|||||||
incorrectPasswordMessage=Das Passwort ist falsch.
|
incorrectPasswordMessage=Das Passwort ist falsch.
|
||||||
usernameExistsMessage=Neuer Benutzername existiert bereits.
|
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.
|
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.
|
confirmPasswordErrorMessage=„Neues Passwort“ und „Neues Passwort bestätigen“ müssen übereinstimmen.
|
||||||
deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht werden.
|
deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht werden.
|
||||||
deleteUsernameExistsMessage=Der Benutzername existiert nicht und 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
|
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.
|
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.
|
userAlreadyExistsOAuthMessage=Der Benutzer ist bereits als OAuth2-Benutzer vorhanden.
|
||||||
userAlreadyExistsWebMessage=Der Benutzer ist bereits als Webbenutzer vorhanden.
|
userAlreadyExistsWebMessage=Der Benutzer ist bereits als Webbenutzer vorhanden.
|
||||||
@@ -177,10 +179,11 @@ adminUserSettings.user=Benutzer
|
|||||||
adminUserSettings.addUser=Neuen Benutzer hinzufügen
|
adminUserSettings.addUser=Neuen Benutzer hinzufügen
|
||||||
adminUserSettings.deleteUser=Benutzer löschen
|
adminUserSettings.deleteUser=Benutzer löschen
|
||||||
adminUserSettings.confirmDeleteUser=Soll der Benutzer gelöscht werden?
|
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.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.roles=Rollen
|
||||||
adminUserSettings.role=Rolle
|
adminUserSettings.role=Rolle
|
||||||
adminUserSettings.actions=Aktion
|
adminUserSettings.actions=Aktions
|
||||||
adminUserSettings.apiUser=Eingeschränkter API-Benutzer
|
adminUserSettings.apiUser=Eingeschränkter API-Benutzer
|
||||||
adminUserSettings.extraApiUser=Zusätzlicher eingeschränkter API-Benutzer
|
adminUserSettings.extraApiUser=Zusätzlicher eingeschränkter API-Benutzer
|
||||||
adminUserSettings.webOnlyUser=Nur Web-Benutzer
|
adminUserSettings.webOnlyUser=Nur Web-Benutzer
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei
|
|||||||
adminUserSettings.submit=Benutzer speichern
|
adminUserSettings.submit=Benutzer speichern
|
||||||
adminUserSettings.changeUserRole=Benutzerrolle ändern
|
adminUserSettings.changeUserRole=Benutzerrolle ändern
|
||||||
adminUserSettings.authenticated=Authentifiziert
|
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
|
database.title=Datenbank Import/Export
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=komprimieren,verkleinern,minimieren
|
|||||||
|
|
||||||
home.changeMetadata.title=Metadaten ändern
|
home.changeMetadata.title=Metadaten ändern
|
||||||
home.changeMetadata.desc=Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument
|
home.changeMetadata.desc=Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument
|
||||||
changeMetadata.tags==titel,autor,datum,erstellung,uhrzeit,herausgeber,produzent,statistiken
|
changeMetadata.tags=titel,autor,datum,erstellung,uhrzeit,herausgeber,produzent,statistiken
|
||||||
|
|
||||||
home.fileToPDF.title=Datei in PDF konvertieren
|
home.fileToPDF.title=Datei in PDF konvertieren
|
||||||
home.fileToPDF.desc=Konvertieren Sie nahezu jede Datei in PDF (DOCX, PNG, XLS, PPT, TXT und mehr)
|
home.fileToPDF.desc=Konvertieren Sie nahezu jede Datei in PDF (DOCX, PNG, XLS, PPT, TXT und mehr)
|
||||||
@@ -481,12 +491,14 @@ login.locked=Ihr Konto wurde gesperrt.
|
|||||||
login.signinTitle=Bitte melden Sie sich an.
|
login.signinTitle=Bitte melden Sie sich an.
|
||||||
login.ssoSignIn=Anmeldung per Single Sign-On
|
login.ssoSignIn=Anmeldung per Single Sign-On
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Benutzer automatisch erstellen deaktiviert
|
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.oauth2RequestNotFound=Autorisierungsanfrage nicht gefunden
|
||||||
login.oauth2InvalidUserInfoResponse=Ungültige Benutzerinformationsantwort
|
login.oauth2InvalidUserInfoResponse=Ungültige Benutzerinformationsantwort
|
||||||
login.oauth2invalidRequest=ungültige Anfrage
|
login.oauth2invalidRequest=ungültige Anfrage
|
||||||
login.oauth2AccessDenied=Zugriff abgelehnt
|
login.oauth2AccessDenied=Zugriff abgelehnt
|
||||||
login.oauth2InvalidTokenResponse=Ungültige Token-Antwort
|
login.oauth2InvalidTokenResponse=Ungültige Token-Antwort
|
||||||
login.oauth2InvalidIdToken=Ungültiges ID-Token
|
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
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimaler Konturbereich:
|
|||||||
ScannerImageSplit.selectText.8=Legt den minimalen Konturbereichsschwellenwert für ein Foto fest
|
ScannerImageSplit.selectText.8=Legt den minimalen Konturbereichsschwellenwert für ein Foto fest
|
||||||
ScannerImageSplit.selectText.9=Randgröße:
|
ScannerImageSplit.selectText.9=Randgröße:
|
||||||
ScannerImageSplit.selectText.10=Legt die Größe des hinzugefügten und entfernten Randes fest, um weiße Ränder in der Ausgabe zu verhindern (Standard: 1).
|
ScannerImageSplit.selectText.10=Legt die Größe des hinzugefügten und entfernten Randes fest, um weiße Ränder in der Ausgabe zu verhindern (Standard: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Farbe
|
|||||||
pdfToImage.grey=Graustufen
|
pdfToImage.grey=Graustufen
|
||||||
pdfToImage.blackwhite=Schwarzweiß (Datenverlust möglich!)
|
pdfToImage.blackwhite=Schwarzweiß (Datenverlust möglich!)
|
||||||
pdfToImage.submit=Umwandeln
|
pdfToImage.submit=Umwandeln
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Ο χρήστης δεν βρέθηκε.
|
|||||||
incorrectPasswordMessage=Ο τρέχων κωδικός πρόσβασης είναι λανθασμένος.
|
incorrectPasswordMessage=Ο τρέχων κωδικός πρόσβασης είναι λανθασμένος.
|
||||||
usernameExistsMessage=Το νέο όνομα χρήστη υπάρχει ήδη.
|
usernameExistsMessage=Το νέο όνομα χρήστη υπάρχει ήδη.
|
||||||
invalidUsernameMessage=Μη έγκυρο όνομα χρήστη, όνομα χρήστη μπορεί να περιέχει μόνο γράμματα, αριθμούς και τους ακόλουθους ειδικούς χαρακτήρες @._+- ή πρέπει να είναι έγκυρη διεύθυνση email.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Δεν είναι δυνατή η διαγραφή του τρέχοντος συνδεδεμένου χρήστη.
|
deleteCurrentUserMessage=Δεν είναι δυνατή η διαγραφή του τρέχοντος συνδεδεμένου χρήστη.
|
||||||
deleteUsernameExistsMessage=Το όνομα χρήστη δεν υπάρχει και δεν μπορεί να διαγραφεί.
|
deleteUsernameExistsMessage=Το όνομα χρήστη δεν υπάρχει και δεν μπορεί να διαγραφεί.
|
||||||
downgradeCurrentUserMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη
|
downgradeCurrentUserMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη
|
||||||
|
disabledCurrentUserMessage=The current user cannot be disabled
|
||||||
downgradeCurrentUserLongMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη. Ως εκ τούτου, ο τρέχων χρήστης δεν θα εμφανίζεται.
|
downgradeCurrentUserLongMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη. Ως εκ τούτου, ο τρέχων χρήστης δεν θα εμφανίζεται.
|
||||||
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Χρήστης
|
|||||||
adminUserSettings.addUser=Προσθήκη νέου Χρήστη
|
adminUserSettings.addUser=Προσθήκη νέου Χρήστη
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||||
adminUserSettings.roles=Ρόλοι
|
adminUserSettings.roles=Ρόλοι
|
||||||
adminUserSettings.role=Ρόλος
|
adminUserSettings.role=Ρόλος
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Αναγκάστε τον χρήστη να αλλ
|
|||||||
adminUserSettings.submit=Αποθήκευση Χρήστη
|
adminUserSettings.submit=Αποθήκευση Χρήστη
|
||||||
adminUserSettings.changeUserRole=Αλλαγή ρόλου χρήστη
|
adminUserSettings.changeUserRole=Αλλαγή ρόλου χρήστη
|
||||||
adminUserSettings.authenticated=Authenticated
|
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
|
database.title=Database Import/Export
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=squish,small,tiny
|
|||||||
|
|
||||||
home.changeMetadata.title=Αλλαγή Μεταδεδομένων
|
home.changeMetadata.title=Αλλαγή Μεταδεδομένων
|
||||||
home.changeMetadata.desc=Αλλαγή/Κατάργηση/Προσθήκη μεταδεδομένων από ένα έγγραφο PDF.
|
home.changeMetadata.desc=Αλλαγή/Κατάργηση/Προσθήκη μεταδεδομένων από ένα έγγραφο PDF.
|
||||||
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=Μετατροπή ενός αρχείου σε PDF
|
home.fileToPDF.title=Μετατροπή ενός αρχείου σε PDF
|
||||||
home.fileToPDF.desc=Μετατροπή σχεδόν οποιουδήποτε αρχείου σε PDF (DOCX, PNG, XLS, PPT, TXT and more)
|
home.fileToPDF.desc=Μετατροπή σχεδόν οποιουδήποτε αρχείου σε PDF (DOCX, PNG, XLS, PPT, TXT and more)
|
||||||
@@ -481,12 +491,14 @@ login.locked=Ο λογαριασμός σας έχει κλειδωθεί.
|
|||||||
login.signinTitle=Παρακαλώ, συνδεθείτε
|
login.signinTitle=Παρακαλώ, συνδεθείτε
|
||||||
login.ssoSignIn=Σύνδεση μέσω μοναδικής σύνδεσης
|
login.ssoSignIn=Σύνδεση μέσω μοναδικής σύνδεσης
|
||||||
login.oauth2AutoCreateDisabled=Απενεργοποιήθηκε ο χρήστης αυτόματης δημιουργίας OAUTH2
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Ελάχιστη επιφάνεια περιγρ
|
|||||||
ScannerImageSplit.selectText.8=Ρυθμίζει το ελάχιστο όριο περιγράμματος για μια φωτογραφία
|
ScannerImageSplit.selectText.8=Ρυθμίζει το ελάχιστο όριο περιγράμματος για μια φωτογραφία
|
||||||
ScannerImageSplit.selectText.9=Μέγεθος περιγράμματος:
|
ScannerImageSplit.selectText.9=Μέγεθος περιγράμματος:
|
||||||
ScannerImageSplit.selectText.10=Ορίζει το μέγεθος του περιγράμματος που προστίθεται και αφαιρείται για να αποτρέπονται λευκά περιγράμματα στην έξοδο (προεπιλογή: 1).
|
ScannerImageSplit.selectText.10=Ορίζει το μέγεθος του περιγράμματος που προστίθεται και αφαιρείται για να αποτρέπονται λευκά περιγράμματα στην έξοδο (προεπιλογή: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Χρώμα
|
|||||||
pdfToImage.grey=Κλίμακα του γκρι
|
pdfToImage.grey=Κλίμακα του γκρι
|
||||||
pdfToImage.blackwhite=Ασπρόμαυρο (Μπορεί να χαθούν δεδομένα!)
|
pdfToImage.blackwhite=Ασπρόμαυρο (Μπορεί να χαθούν δεδομένα!)
|
||||||
pdfToImage.submit=Μετατροπή
|
pdfToImage.submit=Μετατροπή
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=User not found.
|
|||||||
incorrectPasswordMessage=Current password is incorrect.
|
incorrectPasswordMessage=Current password is incorrect.
|
||||||
usernameExistsMessage=New Username already exists.
|
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.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Cannot downgrade current user's role
|
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.
|
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
|
||||||
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=User
|
|||||||
adminUserSettings.addUser=Add New User
|
adminUserSettings.addUser=Add New User
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||||
adminUserSettings.roles=Roles
|
adminUserSettings.roles=Roles
|
||||||
adminUserSettings.role=Role
|
adminUserSettings.role=Role
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Force user to change password on login
|
|||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=Change User's Role
|
adminUserSettings.changeUserRole=Change User's Role
|
||||||
adminUserSettings.authenticated=Authenticated
|
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
|
database.title=Database Import/Export
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=squish,small,tiny
|
|||||||
|
|
||||||
home.changeMetadata.title=Change Metadata
|
home.changeMetadata.title=Change Metadata
|
||||||
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
|
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
|
||||||
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=Convert file to PDF
|
home.fileToPDF.title=Convert file to PDF
|
||||||
home.fileToPDF.desc=Convert nearly any file to PDF (DOCX, PNG, XLS, PPT, TXT and more)
|
home.fileToPDF.desc=Convert nearly any file to PDF (DOCX, PNG, XLS, PPT, TXT and more)
|
||||||
@@ -481,12 +491,14 @@ login.locked=Your account has been locked.
|
|||||||
login.signinTitle=Please sign in
|
login.signinTitle=Please sign in
|
||||||
login.ssoSignIn=Login via Single Sign-on
|
login.ssoSignIn=Login via Single Sign-on
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimum Contour Area:
|
|||||||
ScannerImageSplit.selectText.8=Sets the minimum contour area threshold for a photo
|
ScannerImageSplit.selectText.8=Sets the minimum contour area threshold for a photo
|
||||||
ScannerImageSplit.selectText.9=Border Size:
|
ScannerImageSplit.selectText.9=Border Size:
|
||||||
ScannerImageSplit.selectText.10=Sets the size of the border added and removed to prevent white borders in the output (default: 1).
|
ScannerImageSplit.selectText.10=Sets the size of the border added and removed to prevent white borders in the output (default: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Colour
|
|||||||
pdfToImage.grey=Greyscale
|
pdfToImage.grey=Greyscale
|
||||||
pdfToImage.blackwhite=Black and White (May lose data!)
|
pdfToImage.blackwhite=Black and White (May lose data!)
|
||||||
pdfToImage.submit=Convert
|
pdfToImage.submit=Convert
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1135,4 +1149,4 @@ error.discordSubmit=Discord - Submit Support post
|
|||||||
removeImage.title=Remove image
|
removeImage.title=Remove image
|
||||||
removeImage.header=Remove image
|
removeImage.header=Remove image
|
||||||
removeImage.removeImage=Remove image
|
removeImage.removeImage=Remove image
|
||||||
removeImage.submit=Remove image
|
removeImage.submit=Remove image
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=User not found.
|
|||||||
incorrectPasswordMessage=Current password is incorrect.
|
incorrectPasswordMessage=Current password is incorrect.
|
||||||
usernameExistsMessage=New Username already exists.
|
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.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Cannot downgrade current user's role
|
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.
|
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
|
||||||
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=User
|
|||||||
adminUserSettings.addUser=Add New User
|
adminUserSettings.addUser=Add New User
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||||
adminUserSettings.roles=Roles
|
adminUserSettings.roles=Roles
|
||||||
adminUserSettings.role=Role
|
adminUserSettings.role=Role
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Force user to change password on login
|
|||||||
adminUserSettings.submit=Save User
|
adminUserSettings.submit=Save User
|
||||||
adminUserSettings.changeUserRole=Change User's Role
|
adminUserSettings.changeUserRole=Change User's Role
|
||||||
adminUserSettings.authenticated=Authenticated
|
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
|
database.title=Database Import/Export
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=squish,small,tiny
|
|||||||
|
|
||||||
home.changeMetadata.title=Change Metadata
|
home.changeMetadata.title=Change Metadata
|
||||||
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
|
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
|
||||||
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=Convert file to PDF
|
home.fileToPDF.title=Convert file to PDF
|
||||||
home.fileToPDF.desc=Convert nearly any file to PDF (DOCX, PNG, XLS, PPT, TXT and more)
|
home.fileToPDF.desc=Convert nearly any file to PDF (DOCX, PNG, XLS, PPT, TXT and more)
|
||||||
@@ -481,12 +491,14 @@ login.locked=Your account has been locked.
|
|||||||
login.signinTitle=Please sign in
|
login.signinTitle=Please sign in
|
||||||
login.ssoSignIn=Login via Single Sign-on
|
login.ssoSignIn=Login via Single Sign-on
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimum Contour Area:
|
|||||||
ScannerImageSplit.selectText.8=Sets the minimum contour area threshold for a photo
|
ScannerImageSplit.selectText.8=Sets the minimum contour area threshold for a photo
|
||||||
ScannerImageSplit.selectText.9=Border Size:
|
ScannerImageSplit.selectText.9=Border Size:
|
||||||
ScannerImageSplit.selectText.10=Sets the size of the border added and removed to prevent white borders in the output (default: 1).
|
ScannerImageSplit.selectText.10=Sets the size of the border added and removed to prevent white borders in the output (default: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Color
|
|||||||
pdfToImage.grey=Grayscale
|
pdfToImage.grey=Grayscale
|
||||||
pdfToImage.blackwhite=Black and White (May lose data!)
|
pdfToImage.blackwhite=Black and White (May lose data!)
|
||||||
pdfToImage.submit=Convert
|
pdfToImage.submit=Convert
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Usuario no encontrado.
|
|||||||
incorrectPasswordMessage=La contraseña actual no es correcta.
|
incorrectPasswordMessage=La contraseña actual no es correcta.
|
||||||
usernameExistsMessage=El nuevo nombre de usuario está en uso.
|
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.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=No puede eliminar el usuario que tiene la sesión actualmente en uso.
|
deleteCurrentUserMessage=No puede eliminar el usuario que tiene la sesión actualmente en uso.
|
||||||
deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse.
|
deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse.
|
||||||
downgradeCurrentUserMessage=No se puede degradar el rol del usuario actual
|
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á.
|
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.
|
userAlreadyExistsOAuthMessage=La usuario ya existe como usuario de OAuth2.
|
||||||
userAlreadyExistsWebMessage=El usuario ya existe como usuario web.
|
userAlreadyExistsWebMessage=El usuario ya existe como usuario web.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Usuario
|
|||||||
adminUserSettings.addUser=Añadir Nuevo Usuario
|
adminUserSettings.addUser=Añadir Nuevo Usuario
|
||||||
adminUserSettings.deleteUser=Eliminar Usuario
|
adminUserSettings.deleteUser=Eliminar Usuario
|
||||||
adminUserSettings.confirmDeleteUser=¿Se debe eliminar al usuario?
|
adminUserSettings.confirmDeleteUser=¿Se debe eliminar al usuario?
|
||||||
|
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||||
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.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.roles=Roles
|
||||||
adminUserSettings.role=Rol
|
adminUserSettings.role=Rol
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el
|
|||||||
adminUserSettings.submit=Guardar Usuario
|
adminUserSettings.submit=Guardar Usuario
|
||||||
adminUserSettings.changeUserRole=Cambiar rol de usuario
|
adminUserSettings.changeUserRole=Cambiar rol de usuario
|
||||||
adminUserSettings.authenticated=Autenticado
|
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
|
||||||
|
|
||||||
|
|
||||||
database.title=Base de Datos Importar/Exportar
|
database.title=Base de Datos Importar/Exportar
|
||||||
@@ -481,12 +491,14 @@ login.locked=Su cuenta se ha bloqueado.
|
|||||||
login.signinTitle=Por favor, inicie sesión
|
login.signinTitle=Por favor, inicie sesión
|
||||||
login.ssoSignIn=Iniciar sesión a través del inicio de sesión único
|
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.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.oauth2RequestNotFound=Solicitud de autorización no encontrada
|
||||||
login.oauth2InvalidUserInfoResponse=Respuesta de información de usuario no válida
|
login.oauth2InvalidUserInfoResponse=Respuesta de información de usuario no válida
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Respuesta de token no válida
|
login.oauth2InvalidTokenResponse=Respuesta de token no válida
|
||||||
login.oauth2InvalidIdToken=Token de identificación no válido
|
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.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Área mínima de contorno:
|
|||||||
ScannerImageSplit.selectText.8=Establecer el umbral mínimo del área de contorno para una foto
|
ScannerImageSplit.selectText.8=Establecer el umbral mínimo del área de contorno para una foto
|
||||||
ScannerImageSplit.selectText.9=Tamaño del borde:
|
ScannerImageSplit.selectText.9=Tamaño del borde:
|
||||||
ScannerImageSplit.selectText.10=Establece el tamaño del borde agregado y eliminado para evitar bordes blancos en la salida (predeterminado: 1).
|
ScannerImageSplit.selectText.10=Establece el tamaño del borde agregado y eliminado para evitar bordes blancos en la salida (predeterminado: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Color
|
|||||||
pdfToImage.grey=Escala de grises
|
pdfToImage.grey=Escala de grises
|
||||||
pdfToImage.blackwhite=Blanco y Negro (¡Puede perder datos!)
|
pdfToImage.blackwhite=Blanco y Negro (¡Puede perder datos!)
|
||||||
pdfToImage.submit=Convertir
|
pdfToImage.submit=Convertir
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=User not found.
|
|||||||
incorrectPasswordMessage=Current password is incorrect.
|
incorrectPasswordMessage=Current password is incorrect.
|
||||||
usernameExistsMessage=New Username already exists.
|
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.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Ezin da uneko erabiltzailearen rola jaitsi
|
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.
|
downgradeCurrentUserLongMessage=Ezin da uneko erabiltzailearen rola jaitsi. Beraz, oraingo erabiltzailea ez da erakutsiko.
|
||||||
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Erabiltzaile
|
|||||||
adminUserSettings.addUser=Erabiltzaile berria
|
adminUserSettings.addUser=Erabiltzaile berria
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||||
adminUserSettings.roles=Rolak
|
adminUserSettings.roles=Rolak
|
||||||
adminUserSettings.role=Rol
|
adminUserSettings.role=Rol
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Force user to change password on login
|
|||||||
adminUserSettings.submit=Gorde Erabiltzailea
|
adminUserSettings.submit=Gorde Erabiltzailea
|
||||||
adminUserSettings.changeUserRole=Erabiltzailearen rola aldatu
|
adminUserSettings.changeUserRole=Erabiltzailearen rola aldatu
|
||||||
adminUserSettings.authenticated=Authenticated
|
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
|
database.title=Database Import/Export
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=squish,small,tiny
|
|||||||
|
|
||||||
home.changeMetadata.title=Aldatu metadatuak
|
home.changeMetadata.title=Aldatu metadatuak
|
||||||
home.changeMetadata.desc=Aldatu/Ezabatu/Gehitu metadatuak PDF dokumentuari
|
home.changeMetadata.desc=Aldatu/Ezabatu/Gehitu metadatuak PDF dokumentuari
|
||||||
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=Fitxategia PDF bihurtu
|
home.fileToPDF.title=Fitxategia PDF bihurtu
|
||||||
home.fileToPDF.desc=PDF bihurtu ia edozein fitxategi (DOCX, PNG, XLS, PPT, TXT eta gehiago)
|
home.fileToPDF.desc=PDF bihurtu ia edozein fitxategi (DOCX, PNG, XLS, PPT, TXT eta gehiago)
|
||||||
@@ -481,12 +491,14 @@ login.locked=Zure kontua blokeatu egin da.
|
|||||||
login.signinTitle=Mesedez, hasi saioa
|
login.signinTitle=Mesedez, hasi saioa
|
||||||
login.ssoSignIn=Hasi saioa Saioa hasteko modu bakarraren bidez
|
login.ssoSignIn=Hasi saioa Saioa hasteko modu bakarraren bidez
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Sortu automatikoki erabiltzailea desgaituta dago
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Inguruko area gutxienekoa:
|
|||||||
ScannerImageSplit.selectText.8=Ezarri inguruko arearen gutxieneko balioa argazki batentzat
|
ScannerImageSplit.selectText.8=Ezarri inguruko arearen gutxieneko balioa argazki batentzat
|
||||||
ScannerImageSplit.selectText.9=Ertzaren tamaina:
|
ScannerImageSplit.selectText.9=Ertzaren tamaina:
|
||||||
ScannerImageSplit.selectText.10=Ezarri gehitutako eta ezabatutako ertzaren tamaina irteeran ertz zuriak saihesteko (lehenetsia: 1).
|
ScannerImageSplit.selectText.10=Ezarri gehitutako eta ezabatutako ertzaren tamaina irteeran ertz zuriak saihesteko (lehenetsia: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Kolorea
|
|||||||
pdfToImage.grey=Gris-eskala
|
pdfToImage.grey=Gris-eskala
|
||||||
pdfToImage.blackwhite=Zuria eta Beltza (Datuak galdu ditzake!)
|
pdfToImage.blackwhite=Zuria eta Beltza (Datuak galdu ditzake!)
|
||||||
pdfToImage.submit=Bihurtu
|
pdfToImage.submit=Bihurtu
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Utilisateur non trouvé.
|
|||||||
incorrectPasswordMessage=Le mot de passe actuel est incorrect.
|
incorrectPasswordMessage=Le mot de passe actuel est incorrect.
|
||||||
usernameExistsMessage=Le nouveau nom d’utilisateur existe déjà.
|
usernameExistsMessage=Le nouveau nom d’utilisateur existe déjà.
|
||||||
invalidUsernameMessage=Nom d’utilisateur invalide, 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.
|
invalidUsernameMessage=Nom d’utilisateur invalide, 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.
|
||||||
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Impossible de supprimer l’utilisateur actuellement connecté.
|
deleteCurrentUserMessage=Impossible de supprimer l’utilisateur actuellement connecté.
|
||||||
deleteUsernameExistsMessage=Le nom d’utilisateur n’existe pas et ne peut pas être supprimé.
|
deleteUsernameExistsMessage=Le nom d’utilisateur n’existe pas et ne peut pas être supprimé.
|
||||||
downgradeCurrentUserMessage=Impossible de rétrograder le rôle de l'utilisateur actuel.
|
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é.
|
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.
|
userAlreadyExistsOAuthMessage=L'utilisateur existe déjà en tant qu'utilisateur OAuth2.
|
||||||
userAlreadyExistsWebMessage=L'utilisateur existe déjà en tant qu'utilisateur Web.
|
userAlreadyExistsWebMessage=L'utilisateur existe déjà en tant qu'utilisateur Web.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Utilisateur
|
|||||||
adminUserSettings.addUser=Ajouter un utilisateur
|
adminUserSettings.addUser=Ajouter un utilisateur
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.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.roles=Rôles
|
||||||
adminUserSettings.role=Rôle
|
adminUserSettings.role=Rôle
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Forcer l’utilisateur à changer son nom d’util
|
|||||||
adminUserSettings.submit=Ajouter
|
adminUserSettings.submit=Ajouter
|
||||||
adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur
|
adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur
|
||||||
adminUserSettings.authenticated=Authentifié
|
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
|
database.title=Database Import/Export
|
||||||
@@ -481,12 +491,14 @@ login.locked=Votre compte a été verrouillé.
|
|||||||
login.signinTitle=Veuillez vous connecter
|
login.signinTitle=Veuillez vous connecter
|
||||||
login.ssoSignIn=Se connecter via l'authentification unique
|
login.ssoSignIn=Se connecter via l'authentification unique
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Création automatique d'utilisateur désactivée
|
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.oauth2RequestNotFound=Demande d'autorisation introuvable
|
||||||
login.oauth2InvalidUserInfoResponse=Réponse contenant les informations de l'utilisateur est invalide
|
login.oauth2InvalidUserInfoResponse=Réponse contenant les informations de l'utilisateur est invalide
|
||||||
login.oauth2invalidRequest=Requête invalide
|
login.oauth2invalidRequest=Requête invalide
|
||||||
login.oauth2AccessDenied=Accès refusé
|
login.oauth2AccessDenied=Accès refusé
|
||||||
login.oauth2InvalidTokenResponse=Réponse contenant le jeton est invalide
|
login.oauth2InvalidTokenResponse=Réponse contenant le jeton est invalide
|
||||||
login.oauth2InvalidIdToken=Jeton d'identification 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
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Surface de contour minimale
|
|||||||
ScannerImageSplit.selectText.8=Définit la surface de contour minimale pour une photo (par défaut : 500).
|
ScannerImageSplit.selectText.8=Définit la surface de contour minimale pour une photo (par défaut : 500).
|
||||||
ScannerImageSplit.selectText.9=Taille de la bordure
|
ScannerImageSplit.selectText.9=Taille de la bordure
|
||||||
ScannerImageSplit.selectText.10=Définit la taille de la bordure ajoutée et supprimée pour éviter les bordures blanches dans la sortie (par défaut : 1).
|
ScannerImageSplit.selectText.10=Définit la taille de la bordure ajoutée et supprimée pour éviter les bordures blanches dans la sortie (par défaut : 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Couleur
|
|||||||
pdfToImage.grey=Niveaux de gris
|
pdfToImage.grey=Niveaux de gris
|
||||||
pdfToImage.blackwhite=Noir et blanc (peut engendrer une perte de données !)
|
pdfToImage.blackwhite=Noir et blanc (peut engendrer une perte de données !)
|
||||||
pdfToImage.submit=Convertir
|
pdfToImage.submit=Convertir
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
###########
|
###########
|
||||||
# Generic #
|
# Generic #
|
||||||
###########
|
###########
|
||||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||||
language.direction=ltr
|
language.direction=ltr
|
||||||
|
|
||||||
pdfPrompt=Roghnaigh PDF(s)
|
pdfPrompt=Roghnaigh PDF(s)
|
||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Úsáideoir gan aimsiú.
|
|||||||
incorrectPasswordMessage=Tá an pasfhocal reatha mícheart.
|
incorrectPasswordMessage=Tá an pasfhocal reatha mícheart.
|
||||||
usernameExistsMessage=Tá Ainm Úsáideora Nua ann cheana féin.
|
usernameExistsMessage=Tá Ainm Úsáideora Nua ann cheana féin.
|
||||||
invalidUsernameMessage=Ainm úsáideora neamhbhailí, ní féidir ach litreacha, uimhreacha agus na carachtair speisialta seo a leanas @._+- a bheith san ainm úsáideora nó ní mór gur seoladh ríomhphoist bailí é.
|
invalidUsernameMessage=Ainm úsáideora neamhbhailí, ní féidir ach litreacha, uimhreacha agus na carachtair speisialta seo a leanas @._+- a bheith san ainm úsáideora nó ní mór gur seoladh ríomhphoist bailí é.
|
||||||
|
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
|
||||||
confirmPasswordErrorMessage=Ní mór Pasfhocal Nua agus Deimhnigh Pasfhocal Nua a bheith ag teacht leis.
|
confirmPasswordErrorMessage=Ní mór Pasfhocal Nua agus Deimhnigh Pasfhocal Nua a bheith ag teacht leis.
|
||||||
deleteCurrentUserMessage=Ní féidir an t-úsáideoir atá logáilte isteach faoi láthair a scriosadh.
|
deleteCurrentUserMessage=Ní féidir an t-úsáideoir atá logáilte isteach faoi láthair a scriosadh.
|
||||||
deleteUsernameExistsMessage=Níl an t-ainm úsáideora ann agus ní féidir é a scriosadh.
|
deleteUsernameExistsMessage=Níl an t-ainm úsáideora ann agus ní féidir é a scriosadh.
|
||||||
downgradeCurrentUserMessage=Ní féidir ról an úsáideora reatha a íosghrádú
|
downgradeCurrentUserMessage=Ní féidir ról an úsáideora reatha a íosghrádú
|
||||||
|
disabledCurrentUserMessage=The current user cannot be disabled
|
||||||
downgradeCurrentUserLongMessage=Ní féidir ról an úsáideora reatha a íosghrádú. Mar sin, ní thaispeánfar an t-úsáideoir reatha.
|
downgradeCurrentUserLongMessage=Ní féidir ról an úsáideora reatha a íosghrádú. Mar sin, ní thaispeánfar an t-úsáideoir reatha.
|
||||||
userAlreadyExistsOAuthMessage=Tá an t-úsáideoir ann cheana mar úsáideoir OAuth2.
|
userAlreadyExistsOAuthMessage=Tá an t-úsáideoir ann cheana mar úsáideoir OAuth2.
|
||||||
userAlreadyExistsWebMessage=Tá an t-úsáideoir ann cheana féin mar úsáideoir gréasáin.
|
userAlreadyExistsWebMessage=Tá an t-úsáideoir ann cheana féin mar úsáideoir gréasáin.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Úsáideoir
|
|||||||
adminUserSettings.addUser=Cuir Úsáideoir Nua leis
|
adminUserSettings.addUser=Cuir Úsáideoir Nua leis
|
||||||
adminUserSettings.deleteUser=Scrios Úsáideoir
|
adminUserSettings.deleteUser=Scrios Úsáideoir
|
||||||
adminUserSettings.confirmDeleteUser=Ar cheart an t-úsáideoir a scriosadh?
|
adminUserSettings.confirmDeleteUser=Ar cheart an t-úsáideoir a scriosadh?
|
||||||
|
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||||
adminUserSettings.usernameInfo=Ní féidir ach litreacha, uimhreacha agus na carachtair speisialta seo a leanas @._+- a bheith san ainm úsáideora nó ní mór gur seoladh ríomhphoist bailí é.
|
adminUserSettings.usernameInfo=Ní féidir ach litreacha, uimhreacha agus na carachtair speisialta seo a leanas @._+- a bheith san ainm úsáideora nó ní mór gur seoladh ríomhphoist bailí é.
|
||||||
adminUserSettings.roles=Róil
|
adminUserSettings.roles=Róil
|
||||||
adminUserSettings.role=Ról
|
adminUserSettings.role=Ról
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Cuir iallach ar an úsáideoir pasfhocal a athrú
|
|||||||
adminUserSettings.submit=Sábháil Úsáideoir
|
adminUserSettings.submit=Sábháil Úsáideoir
|
||||||
adminUserSettings.changeUserRole=Athraigh Ról an Úsáideora
|
adminUserSettings.changeUserRole=Athraigh Ról an Úsáideora
|
||||||
adminUserSettings.authenticated=Fíordheimhnithe
|
adminUserSettings.authenticated=Fíordheimhnithe
|
||||||
|
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=Iompórtáil / Easpórtáil Bunachar Sonraí
|
database.title=Iompórtáil / Easpórtáil Bunachar Sonraí
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=squish, beag, beag bídeach
|
|||||||
|
|
||||||
home.changeMetadata.title=Athraigh Meiteashonraí
|
home.changeMetadata.title=Athraigh Meiteashonraí
|
||||||
home.changeMetadata.desc=Athraigh/Bain/Cuir meiteashonraí ó dhoiciméad PDF
|
home.changeMetadata.desc=Athraigh/Bain/Cuir meiteashonraí ó dhoiciméad PDF
|
||||||
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=Comhad a thiontú go PDF
|
home.fileToPDF.title=Comhad a thiontú go PDF
|
||||||
home.fileToPDF.desc=Tiontaigh beagnach aon chomhad go PDF (DOCX, PNG, XLS, PPT, TXT agus go leor eile)
|
home.fileToPDF.desc=Tiontaigh beagnach aon chomhad go PDF (DOCX, PNG, XLS, PPT, TXT agus go leor eile)
|
||||||
@@ -481,12 +491,14 @@ login.locked=Tá do chuntas glasáilte.
|
|||||||
login.signinTitle=Sínigh isteach le do thoil
|
login.signinTitle=Sínigh isteach le do thoil
|
||||||
login.ssoSignIn=Logáil isteach trí Chlárú Aonair
|
login.ssoSignIn=Logáil isteach trí Chlárú Aonair
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Uath-Chruthaigh Úsáideoir faoi Mhíchumas
|
login.oauth2AutoCreateDisabled=OAUTH2 Uath-Chruthaigh Úsáideoir faoi Mhíchumas
|
||||||
|
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
|
||||||
login.oauth2RequestNotFound=Níor aimsíodh iarratas údaraithe
|
login.oauth2RequestNotFound=Níor aimsíodh iarratas údaraithe
|
||||||
login.oauth2InvalidUserInfoResponse=Freagra Neamhbhailí Faisnéise Úsáideora
|
login.oauth2InvalidUserInfoResponse=Freagra Neamhbhailí Faisnéise Úsáideora
|
||||||
login.oauth2invalidRequest=Iarratas Neamhbhailí
|
login.oauth2invalidRequest=Iarratas Neamhbhailí
|
||||||
login.oauth2AccessDenied=Rochtain Diúltaithe
|
login.oauth2AccessDenied=Rochtain Diúltaithe
|
||||||
login.oauth2InvalidTokenResponse=Freagra Comhartha Neamhbhailí
|
login.oauth2InvalidTokenResponse=Freagra Comhartha Neamhbhailí
|
||||||
login.oauth2InvalidIdToken=Comhartha Aitheantais Neamhbhailí
|
login.oauth2InvalidIdToken=Comhartha Aitheantais Neamhbhailí
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Íos-Limistéar Comhrianta:
|
|||||||
ScannerImageSplit.selectText.8=Socraíonn sé an tairseach íosta achar comhrianta le haghaidh grianghraf
|
ScannerImageSplit.selectText.8=Socraíonn sé an tairseach íosta achar comhrianta le haghaidh grianghraf
|
||||||
ScannerImageSplit.selectText.9=Méid na Teorann:
|
ScannerImageSplit.selectText.9=Méid na Teorann:
|
||||||
ScannerImageSplit.selectText.10=Socraíonn sé méid na teorann a chuirtear leis agus a bhaintear chun teorainneacha bán a chosc san aschur (réamhshocraithe: 1).
|
ScannerImageSplit.selectText.10=Socraíonn sé méid na teorann a chuirtear leis agus a bhaintear chun teorainneacha bán a chosc san aschur (réamhshocraithe: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Dath
|
|||||||
pdfToImage.grey=Scála Liath
|
pdfToImage.grey=Scála Liath
|
||||||
pdfToImage.blackwhite=Dubh agus Bán (D’fhéadfadh sonraí a chailleadh!)
|
pdfToImage.blackwhite=Dubh agus Bán (D’fhéadfadh sonraí a chailleadh!)
|
||||||
pdfToImage.submit=Tiontaigh
|
pdfToImage.submit=Tiontaigh
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=उपयोगकर्ता नहीं मिला।
|
|||||||
incorrectPasswordMessage=वर्तमान पासवर्ड गलत है।
|
incorrectPasswordMessage=वर्तमान पासवर्ड गलत है।
|
||||||
usernameExistsMessage=नया उपयोगकर्ता नाम पहले से मौजूद है।
|
usernameExistsMessage=नया उपयोगकर्ता नाम पहले से मौजूद है।
|
||||||
invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता
|
downgradeCurrentUserMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता
|
||||||
|
disabledCurrentUserMessage=The current user cannot be disabled
|
||||||
downgradeCurrentUserLongMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता। इसलिए, वर्तमान उपयोगकर्ता को नहीं दिखाया जाएगा।
|
downgradeCurrentUserLongMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता। इसलिए, वर्तमान उपयोगकर्ता को नहीं दिखाया जाएगा।
|
||||||
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=उपयोगकर्ता
|
|||||||
adminUserSettings.addUser=नया उपयोगकर्ता जोड़ें
|
adminUserSettings.addUser=नया उपयोगकर्ता जोड़ें
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||||
adminUserSettings.roles=रोल्स
|
adminUserSettings.roles=रोल्स
|
||||||
adminUserSettings.role=रोल
|
adminUserSettings.role=रोल
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=उपयोगकर्ता को लॉगि
|
|||||||
adminUserSettings.submit=उपयोगकर्ता को सहेजें
|
adminUserSettings.submit=उपयोगकर्ता को सहेजें
|
||||||
adminUserSettings.changeUserRole=यूज़र की भूमिका बदलें
|
adminUserSettings.changeUserRole=यूज़र की भूमिका बदलें
|
||||||
adminUserSettings.authenticated=Authenticated
|
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
|
database.title=Database Import/Export
|
||||||
@@ -481,12 +491,14 @@ login.locked=आपका खाता लॉक कर दिया गया
|
|||||||
login.signinTitle=कृपया साइन इन करें
|
login.signinTitle=कृपया साइन इन करें
|
||||||
login.ssoSignIn=सिंगल साइन - ऑन के ज़रिए लॉग इन करें
|
login.ssoSignIn=सिंगल साइन - ऑन के ज़रिए लॉग इन करें
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 ऑटो - क्रिएट यूज़र अक्षम किया गया
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=न्यूनतम कंटोर क्ष
|
|||||||
ScannerImageSplit.selectText.8=फोटो के लिए न्यूनतम कंटोर क्षेत्र थ्रेशोल्ड को सेट करता है।
|
ScannerImageSplit.selectText.8=फोटो के लिए न्यूनतम कंटोर क्षेत्र थ्रेशोल्ड को सेट करता है।
|
||||||
ScannerImageSplit.selectText.9=बॉर्डर का आकार:
|
ScannerImageSplit.selectText.9=बॉर्डर का आकार:
|
||||||
ScannerImageSplit.selectText.10=निकालने और जोड़ने के लिए जोड़ा जाने वाला बॉर्डर का आकार सेट करता है ताकि आउटपुट में सफेद बॉर्डर न आए (डिफ़ॉल्ट: 1)।
|
ScannerImageSplit.selectText.10=निकालने और जोड़ने के लिए जोड़ा जाने वाला बॉर्डर का आकार सेट करता है ताकि आउटपुट में सफेद बॉर्डर न आए (डिफ़ॉल्ट: 1)।
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=रंगीन
|
|||||||
pdfToImage.grey=ग्रे स्केल
|
pdfToImage.grey=ग्रे स्केल
|
||||||
pdfToImage.blackwhite=काला और सफेद (डेटा खो सकता है!)
|
pdfToImage.blackwhite=काला और सफेद (डेटा खो सकता है!)
|
||||||
pdfToImage.submit=परिवर्तित करें
|
pdfToImage.submit=परिवर्तित करें
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Korisnik nije pronađen.
|
|||||||
incorrectPasswordMessage=Kriva zaporka.
|
incorrectPasswordMessage=Kriva zaporka.
|
||||||
usernameExistsMessage=Korisničko ime već postoji
|
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.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Nije moguće izbrisati trenutno prijavljenog korisnika.
|
deleteCurrentUserMessage=Nije moguće izbrisati trenutno prijavljenog korisnika.
|
||||||
deleteUsernameExistsMessage=Korisničko ime ne postoji i ne može se izbrisati.
|
deleteUsernameExistsMessage=Korisničko ime ne postoji i ne može se izbrisati.
|
||||||
downgradeCurrentUserMessage=Nije moguće vratiti unazad ulogu trenutnog korisnika
|
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.
|
downgradeCurrentUserLongMessage=Nije moguće vratiti unazad ulogu trenutnog korisnika. Dakle, trenutni korisnik neće biti prikazan.
|
||||||
userAlreadyExistsOAuthMessage=Korisnik već postoji kao OAuth2 korisnik.
|
userAlreadyExistsOAuthMessage=Korisnik već postoji kao OAuth2 korisnik.
|
||||||
userAlreadyExistsWebMessage=Korisnik već postoji kao web korisnik.
|
userAlreadyExistsWebMessage=Korisnik već postoji kao web korisnik.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Korisnik
|
|||||||
adminUserSettings.addUser=Dodaj novog korisnika
|
adminUserSettings.addUser=Dodaj novog korisnika
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.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.roles=Uloge
|
||||||
adminUserSettings.role=Uloga
|
adminUserSettings.role=Uloga
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Prisiliti korisnika da promijeni lozinku prilikom
|
|||||||
adminUserSettings.submit=Spremi korisnika
|
adminUserSettings.submit=Spremi korisnika
|
||||||
adminUserSettings.changeUserRole=Promijenite korisničku ulogu
|
adminUserSettings.changeUserRole=Promijenite korisničku ulogu
|
||||||
adminUserSettings.authenticated=Autentificirano
|
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
|
database.title=Database Import/Export
|
||||||
@@ -481,12 +491,14 @@ login.locked=Vaš račun je zaključan.
|
|||||||
login.signinTitle=Molimo vas da se prijavite
|
login.signinTitle=Molimo vas da se prijavite
|
||||||
login.ssoSignIn=Prijavite se putem jedinstvene prijave
|
login.ssoSignIn=Prijavite se putem jedinstvene prijave
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 automatsko kreiranje korisnika je onemogućeno
|
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.oauth2RequestNotFound=Zahtjev za autorizaciju nije pronađen
|
||||||
login.oauth2InvalidUserInfoResponse=Nevažeće informacije o korisniku
|
login.oauth2InvalidUserInfoResponse=Nevažeće informacije o korisniku
|
||||||
login.oauth2invalidRequest=Neispravan zahtjev
|
login.oauth2invalidRequest=Neispravan zahtjev
|
||||||
login.oauth2AccessDenied=Pristup odbijen
|
login.oauth2AccessDenied=Pristup odbijen
|
||||||
login.oauth2InvalidTokenResponse=Nevažeći odgovor tokena
|
login.oauth2InvalidTokenResponse=Nevažeći odgovor tokena
|
||||||
login.oauth2InvalidIdToken=Nevažeći ID token
|
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
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimalna konturna površina:
|
|||||||
ScannerImageSplit.selectText.8=Postavlja minimalni prag površine konture za fotografiju
|
ScannerImageSplit.selectText.8=Postavlja minimalni prag površine konture za fotografiju
|
||||||
ScannerImageSplit.selectText.9=Veličina obruba:
|
ScannerImageSplit.selectText.9=Veličina obruba:
|
||||||
ScannerImageSplit.selectText.10=Postavlja veličinu obruba koji se dodaje i uklanja kako bi se spriječili bijeli obrubi u ispisu (zadano: 1).
|
ScannerImageSplit.selectText.10=Postavlja veličinu obruba koji se dodaje i uklanja kako bi se spriječili bijeli obrubi u ispisu (zadano: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Boja
|
|||||||
pdfToImage.grey=Sivi tonovi
|
pdfToImage.grey=Sivi tonovi
|
||||||
pdfToImage.blackwhite=Crno-bijelo (mogu se izgubiti podaci!)
|
pdfToImage.blackwhite=Crno-bijelo (mogu se izgubiti podaci!)
|
||||||
pdfToImage.submit=Pretvori
|
pdfToImage.submit=Pretvori
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=A felhasználó nem található.
|
|||||||
incorrectPasswordMessage=A jelenlegi jelszó helytelen.
|
incorrectPasswordMessage=A jelenlegi jelszó helytelen.
|
||||||
usernameExistsMessage=Az új felhasználónév már létezik.
|
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.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=A jelenlegi felhasználó szerepkörét nem lehet visszaminősíteni
|
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.
|
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.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Felhasználó
|
|||||||
adminUserSettings.addUser=Új felhasználó hozzáadása
|
adminUserSettings.addUser=Új felhasználó hozzáadása
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||||
adminUserSettings.roles=Szerepek
|
adminUserSettings.roles=Szerepek
|
||||||
adminUserSettings.role=Szerep
|
adminUserSettings.role=Szerep
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Kényszerítse a felhasználót a felhasználóné
|
|||||||
adminUserSettings.submit=Felhasználó mentése
|
adminUserSettings.submit=Felhasználó mentése
|
||||||
adminUserSettings.changeUserRole=Felhasználó szerepkörének módosítása
|
adminUserSettings.changeUserRole=Felhasználó szerepkörének módosítása
|
||||||
adminUserSettings.authenticated=Authenticated
|
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
|
database.title=Database Import/Export
|
||||||
@@ -481,12 +491,14 @@ login.locked=A fiókja zárolva lett!
|
|||||||
login.signinTitle=Kérjük, jelentkezzen be!
|
login.signinTitle=Kérjük, jelentkezzen be!
|
||||||
login.ssoSignIn=Bejelentkezés egyszeri bejelentkezéssel
|
login.ssoSignIn=Bejelentkezés egyszeri bejelentkezéssel
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Felhasználó automatikus létrehozása letiltva
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimális kontúr terület:
|
|||||||
ScannerImageSplit.selectText.8=A fotók minimális kontúrterületének beállítása
|
ScannerImageSplit.selectText.8=A fotók minimális kontúrterületének beállítása
|
||||||
ScannerImageSplit.selectText.9=Keret mérete:
|
ScannerImageSplit.selectText.9=Keret mérete:
|
||||||
ScannerImageSplit.selectText.10=A hozzáadott és eltávolított keret méretének beállítása a fehér keretek elkerülése érdekében a kimeneten (alapértelmezett: 1).
|
ScannerImageSplit.selectText.10=A hozzáadott és eltávolított keret méretének beállítása a fehér keretek elkerülése érdekében a kimeneten (alapértelmezett: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=színes
|
|||||||
pdfToImage.grey=szürkeárnyalatos
|
pdfToImage.grey=szürkeárnyalatos
|
||||||
pdfToImage.blackwhite=fekete-fehér (adatvesztéssel járhat!)
|
pdfToImage.blackwhite=fekete-fehér (adatvesztéssel járhat!)
|
||||||
pdfToImage.submit=Átalakítás
|
pdfToImage.submit=Átalakítás
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Pengguna tidak ditemukan.
|
|||||||
incorrectPasswordMessage=Kata sandi saat ini salah.
|
incorrectPasswordMessage=Kata sandi saat ini salah.
|
||||||
usernameExistsMessage=Nama pengguna baru sudah ada.
|
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.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
deleteCurrentUserMessage=Cannot delete currently logged in user.
|
||||||
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
|
||||||
downgradeCurrentUserMessage=Tidak dapat menurunkan peran pengguna saat ini
|
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.
|
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.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Pengguna
|
|||||||
adminUserSettings.addUser=Tambahkan Pengguna Baru
|
adminUserSettings.addUser=Tambahkan Pengguna Baru
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
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.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
|
||||||
adminUserSettings.roles=Peran
|
adminUserSettings.roles=Peran
|
||||||
adminUserSettings.role=Peran
|
adminUserSettings.role=Peran
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata
|
|||||||
adminUserSettings.submit=Simpan Pengguna
|
adminUserSettings.submit=Simpan Pengguna
|
||||||
adminUserSettings.changeUserRole=Ubah Peran Pengguna
|
adminUserSettings.changeUserRole=Ubah Peran Pengguna
|
||||||
adminUserSettings.authenticated=Authenticated
|
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
|
database.title=Database Import/Export
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=squish, kecil, kecil
|
|||||||
|
|
||||||
home.changeMetadata.title=Ubah Metadata
|
home.changeMetadata.title=Ubah Metadata
|
||||||
home.changeMetadata.desc=Mengubah/Menghapus/Menambahkan metadata dari dokumen PDF
|
home.changeMetadata.desc=Mengubah/Menghapus/Menambahkan metadata dari dokumen PDF
|
||||||
changeMetadata.tags==Judul,penulis,tanggal,pembuatan,waktu,penerbit,produser,statistik
|
changeMetadata.tags=Judul,penulis,tanggal,pembuatan,waktu,penerbit,produser,statistik
|
||||||
|
|
||||||
home.fileToPDF.title=Mengonversi berkas ke PDF
|
home.fileToPDF.title=Mengonversi berkas ke PDF
|
||||||
home.fileToPDF.desc=Mengonversi hampir semua berkas ke PDF (DOCX, PNG, XLS, PPT, TXT dan lain-lain)
|
home.fileToPDF.desc=Mengonversi hampir semua berkas ke PDF (DOCX, PNG, XLS, PPT, TXT dan lain-lain)
|
||||||
@@ -481,12 +491,14 @@ login.locked=Akun Anda telah dikunci.
|
|||||||
login.signinTitle=Silakan masuk
|
login.signinTitle=Silakan masuk
|
||||||
login.ssoSignIn=Masuk melalui Single Sign - on
|
login.ssoSignIn=Masuk melalui Single Sign - on
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Buat Otomatis Pengguna Dinonaktifkan
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Area Kontur Minimum:
|
|||||||
ScannerImageSplit.selectText.8=Menetapkan ambang batas area kontur minimum untuk foto
|
ScannerImageSplit.selectText.8=Menetapkan ambang batas area kontur minimum untuk foto
|
||||||
ScannerImageSplit.selectText.9=Ukuran Batas:
|
ScannerImageSplit.selectText.9=Ukuran Batas:
|
||||||
ScannerImageSplit.selectText.10=Menetapkan ukuran batas yang ditambahkan dan dihapus untuk mencegah batas putih pada output (default: 1).
|
ScannerImageSplit.selectText.10=Menetapkan ukuran batas yang ditambahkan dan dihapus untuk mencegah batas putih pada output (default: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Warna
|
|||||||
pdfToImage.grey=Skala abu-abu
|
pdfToImage.grey=Skala abu-abu
|
||||||
pdfToImage.blackwhite=Black and White (Bisa kehilangan data!)
|
pdfToImage.blackwhite=Black and White (Bisa kehilangan data!)
|
||||||
pdfToImage.submit=Konversi
|
pdfToImage.submit=Konversi
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Utente non trovato.
|
|||||||
incorrectPasswordMessage=La password attuale non è corretta.
|
incorrectPasswordMessage=La password attuale non è corretta.
|
||||||
usernameExistsMessage=Il nuovo nome utente esiste già.
|
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.
|
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=La password non deve essere vuota e non deve contenere spazi all'inizio o alla fine.
|
||||||
confirmPasswordErrorMessage=La nuova password e la conferma della nuova password devono corrispondere.
|
confirmPasswordErrorMessage=La nuova password e la conferma della nuova password devono corrispondere.
|
||||||
deleteCurrentUserMessage=Impossibile eliminare l'utente attualmente connesso.
|
deleteCurrentUserMessage=Impossibile eliminare l'utente attualmente connesso.
|
||||||
deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato.
|
deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato.
|
||||||
downgradeCurrentUserMessage=Impossibile declassare il ruolo dell'utente corrente
|
downgradeCurrentUserMessage=Impossibile declassare il ruolo dell'utente corrente
|
||||||
|
disabledCurrentUserMessage=L'utente corrente non può essere disabilitato
|
||||||
downgradeCurrentUserLongMessage=Impossibile declassare il ruolo dell'utente corrente. Pertanto, l'utente corrente non verrà visualizzato.
|
downgradeCurrentUserLongMessage=Impossibile declassare il ruolo dell'utente corrente. Pertanto, l'utente corrente non verrà visualizzato.
|
||||||
userAlreadyExistsOAuthMessage=L'utente esiste già come utente OAuth2.
|
userAlreadyExistsOAuthMessage=L'utente esiste già come utente OAuth2.
|
||||||
userAlreadyExistsWebMessage=L'utente esiste già come utente web.
|
userAlreadyExistsWebMessage=L'utente esiste già come utente web.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Utente
|
|||||||
adminUserSettings.addUser=Aggiungi un nuovo Utente
|
adminUserSettings.addUser=Aggiungi un nuovo Utente
|
||||||
adminUserSettings.deleteUser=Elimina utente
|
adminUserSettings.deleteUser=Elimina utente
|
||||||
adminUserSettings.confirmDeleteUser=L'utente deve essere eliminato?
|
adminUserSettings.confirmDeleteUser=L'utente deve essere eliminato?
|
||||||
|
adminUserSettings.confirmChangeUserStatus=L'utente dovrebbe essere disabilitato/abilitato?
|
||||||
adminUserSettings.usernameInfo=Il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- oppure deve essere un indirizzo email valido.
|
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.roles=Ruoli
|
||||||
adminUserSettings.role=Ruolo
|
adminUserSettings.role=Ruolo
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password a
|
|||||||
adminUserSettings.submit=Salva utente
|
adminUserSettings.submit=Salva utente
|
||||||
adminUserSettings.changeUserRole=Cambia il ruolo dell'utente
|
adminUserSettings.changeUserRole=Cambia il ruolo dell'utente
|
||||||
adminUserSettings.authenticated=Autenticato
|
adminUserSettings.authenticated=Autenticato
|
||||||
|
adminUserSettings.editOwnProfil=Modifica il tuo profilo
|
||||||
|
adminUserSettings.enabledUser=utente abilitato
|
||||||
|
adminUserSettings.disabledUser=utente disabilitato
|
||||||
|
adminUserSettings.activeUsers=Utenti attivi:
|
||||||
|
adminUserSettings.disabledUsers=Utenti disabili:
|
||||||
|
adminUserSettings.totalUsers=Utenti totali:
|
||||||
|
adminUserSettings.lastRequest=Ultima richiesta
|
||||||
|
|
||||||
|
|
||||||
database.title=Importazione/Esportazione database
|
database.title=Importazione/Esportazione database
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=comprimere,piccolo,minuscolo
|
|||||||
|
|
||||||
home.changeMetadata.title=Modifica Proprietà
|
home.changeMetadata.title=Modifica Proprietà
|
||||||
home.changeMetadata.desc=Modifica/Aggiungi/Rimuovi le proprietà di un documento PDF.
|
home.changeMetadata.desc=Modifica/Aggiungi/Rimuovi le proprietà di un documento PDF.
|
||||||
changeMetadata.tags==Titolo,autore,data,creazione,ora,editore,produttore,statistiche
|
changeMetadata.tags=Titolo,autore,data,creazione,ora,editore,produttore,statistiche
|
||||||
|
|
||||||
home.fileToPDF.title=Converti file in PDF
|
home.fileToPDF.title=Converti file in PDF
|
||||||
home.fileToPDF.desc=Converti quasi ogni file in PDF (DOCX, PNG, XLS, PPT, TXT e altro)
|
home.fileToPDF.desc=Converti quasi ogni file in PDF (DOCX, PNG, XLS, PPT, TXT e altro)
|
||||||
@@ -461,9 +471,9 @@ home.BookToPDF.title=Libro in PDF
|
|||||||
home.BookToPDF.desc=Converte i formati di libri/fumetti in PDF utilizzando Calibre
|
home.BookToPDF.desc=Converte i formati di libri/fumetti in PDF utilizzando Calibre
|
||||||
BookToPDF.tags=Libro,fumetto,calibre,conversione,manga,amazon,kindle
|
BookToPDF.tags=Libro,fumetto,calibre,conversione,manga,amazon,kindle
|
||||||
|
|
||||||
home.removeImagePdf.title=Remove image
|
home.removeImagePdf.title=Rimuovi immagine
|
||||||
home.removeImagePdf.desc=Remove image from PDF to reduce file size
|
home.removeImagePdf.desc=Rimuovi le immagini dal PDF per ridurre la dimensione del file
|
||||||
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
|
removeImagePdf.tags=Rimuovi immagine,operazioni sulla pagina,back-end,lato server
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
@@ -481,12 +491,14 @@ login.locked=Il tuo account è stato bloccato.
|
|||||||
login.signinTitle=Per favore accedi
|
login.signinTitle=Per favore accedi
|
||||||
login.ssoSignIn=Accedi tramite Single Sign-on
|
login.ssoSignIn=Accedi tramite Single Sign-on
|
||||||
login.oauth2AutoCreateDisabled=Creazione automatica utente OAUTH2 DISABILITATA
|
login.oauth2AutoCreateDisabled=Creazione automatica utente OAUTH2 DISABILITATA
|
||||||
|
login.oauth2AdminBlockedUser=La registrazione o l'accesso degli utenti non registrati è attualmente bloccata. Si prega di contattare l'amministratore.
|
||||||
login.oauth2RequestNotFound=Richiesta di autorizzazione non trovata
|
login.oauth2RequestNotFound=Richiesta di autorizzazione non trovata
|
||||||
login.oauth2InvalidUserInfoResponse=Risposta relativa alle informazioni utente non valida
|
login.oauth2InvalidUserInfoResponse=Risposta relativa alle informazioni utente non valida
|
||||||
login.oauth2invalidRequest=Richiesta non valida
|
login.oauth2invalidRequest=Richiesta non valida
|
||||||
login.oauth2AccessDenied=Accesso negato
|
login.oauth2AccessDenied=Accesso negato
|
||||||
login.oauth2InvalidTokenResponse=Risposta token non valida
|
login.oauth2InvalidTokenResponse=Risposta token non valida
|
||||||
login.oauth2InvalidIdToken=Id Token non valido
|
login.oauth2InvalidIdToken=Id Token non valido
|
||||||
|
login.userIsDisabled=L'utente è disattivato, l'accesso è attualmente bloccato con questo nome utente. Si prega di contattare l'amministratore.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Area di contorno minima:
|
|||||||
ScannerImageSplit.selectText.8=Imposta l'area minima del contorno di una foto
|
ScannerImageSplit.selectText.8=Imposta l'area minima del contorno di una foto
|
||||||
ScannerImageSplit.selectText.9=Spessore bordo:
|
ScannerImageSplit.selectText.9=Spessore bordo:
|
||||||
ScannerImageSplit.selectText.10=Imposta lo spessore del bordo aggiunto o rimosso per prevenire bordi bianchi nel risultato (predefinito: 1).
|
ScannerImageSplit.selectText.10=Imposta lo spessore del bordo aggiunto o rimosso per prevenire bordi bianchi nel risultato (predefinito: 1).
|
||||||
|
ScannerImageSplit.info=Python non è installato. È necessario per l'esecuzione.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=A colori
|
|||||||
pdfToImage.grey=Scala di grigi
|
pdfToImage.grey=Scala di grigi
|
||||||
pdfToImage.blackwhite=Bianco e Nero (potresti perdere dettagli!)
|
pdfToImage.blackwhite=Bianco e Nero (potresti perdere dettagli!)
|
||||||
pdfToImage.submit=Converti
|
pdfToImage.submit=Converti
|
||||||
|
pdfToImage.info=Python non è installato.È richiesto per la conversione WebP.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=ユーザーが見つかりません。
|
|||||||
incorrectPasswordMessage=現在のパスワードが正しくありません。
|
incorrectPasswordMessage=現在のパスワードが正しくありません。
|
||||||
usernameExistsMessage=新しいユーザー名はすでに存在します。
|
usernameExistsMessage=新しいユーザー名はすでに存在します。
|
||||||
invalidUsernameMessage=ユーザー名が無効です。ユーザー名には文字、数字、およびそれに続く特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
|
invalidUsernameMessage=ユーザー名が無効です。ユーザー名には文字、数字、およびそれに続く特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
|
||||||
|
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
|
||||||
confirmPasswordErrorMessage=新しいパスワードと新しいパスワードの確認は一致する必要があります。
|
confirmPasswordErrorMessage=新しいパスワードと新しいパスワードの確認は一致する必要があります。
|
||||||
deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。
|
deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。
|
||||||
deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。
|
deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。
|
||||||
downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません
|
downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません
|
||||||
|
disabledCurrentUserMessage=The current user cannot be disabled
|
||||||
downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。
|
downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。
|
||||||
userAlreadyExistsOAuthMessage=ユーザーは既にOAuth2ユーザーとして存在します。
|
userAlreadyExistsOAuthMessage=ユーザーは既にOAuth2ユーザーとして存在します。
|
||||||
userAlreadyExistsWebMessage=ユーザーは既にWebユーザーとして存在します。
|
userAlreadyExistsWebMessage=ユーザーは既にWebユーザーとして存在します。
|
||||||
@@ -107,7 +109,7 @@ pipelineOptions.validateButton=検証
|
|||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
#############
|
#############
|
||||||
navbar.favorite=Favorites
|
navbar.favorite=お気に入り
|
||||||
navbar.darkmode=ダークモード
|
navbar.darkmode=ダークモード
|
||||||
navbar.language=言語
|
navbar.language=言語
|
||||||
navbar.settings=設定
|
navbar.settings=設定
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=ユーザー
|
|||||||
adminUserSettings.addUser=新しいユーザを追加
|
adminUserSettings.addUser=新しいユーザを追加
|
||||||
adminUserSettings.deleteUser=ユーザの削除
|
adminUserSettings.deleteUser=ユーザの削除
|
||||||
adminUserSettings.confirmDeleteUser=ユーザを本当に削除しますか?
|
adminUserSettings.confirmDeleteUser=ユーザを本当に削除しますか?
|
||||||
|
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||||
adminUserSettings.usernameInfo=ユーザー名には、文字、数字、および次の特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
|
adminUserSettings.usernameInfo=ユーザー名には、文字、数字、および次の特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります。
|
||||||
adminUserSettings.roles=役割
|
adminUserSettings.roles=役割
|
||||||
adminUserSettings.role=役割
|
adminUserSettings.role=役割
|
||||||
@@ -190,23 +193,30 @@ adminUserSettings.forceChange=ログイン時にユーザー名/パスワード
|
|||||||
adminUserSettings.submit=ユーザーの保存
|
adminUserSettings.submit=ユーザーの保存
|
||||||
adminUserSettings.changeUserRole=ユーザーの役割を変更する
|
adminUserSettings.changeUserRole=ユーザーの役割を変更する
|
||||||
adminUserSettings.authenticated=認証済
|
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
|
database.title=データベースのインポート/エクスポート
|
||||||
database.header=Database Import/Export
|
database.header=データベースのインポート/エクスポート
|
||||||
database.fileName=File Name
|
database.fileName=ファイル名
|
||||||
database.creationDate=Creation Date
|
database.creationDate=作成日
|
||||||
database.fileSize=File Size
|
database.fileSize=ファイルサイズ
|
||||||
database.deleteBackupFile=Delete Backup File
|
database.deleteBackupFile=バックアップファイルの削除
|
||||||
database.importBackupFile=Import Backup File
|
database.importBackupFile=バックアップファイルをインポート
|
||||||
database.downloadBackupFile=Download Backup File
|
database.downloadBackupFile=バックアップファイルをダウンロード
|
||||||
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_1=データをインポートする際には、正しい構造を確保することが極めて重要です。不明な点がある場合は、専門家のアドバイスやサポートを受けてください。構造上のエラーは、アプリケーションの誤動作を引き起こす可能性があります。
|
||||||
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.info_2=ファイル名はアップロード時には関係ありません。アップロード後にbackup_user_yyyyMMddHHmm.sqlという形式にリネームされ、一貫した命名規則が保証されます。
|
||||||
database.submit=Import Backup
|
database.submit=バックアップをインポート
|
||||||
database.importIntoDatabaseSuccessed=Import into database successed
|
database.importIntoDatabaseSuccessed=データベースへのインポートに成功
|
||||||
database.fileNotFound=File not Found
|
database.fileNotFound=ファイルが見つかりません
|
||||||
database.fileNullOrEmpty=File must not be null or empty
|
database.fileNullOrEmpty=ファイルは null または空であってはなりません
|
||||||
database.failedImportFile=Failed Import File
|
database.failedImportFile=ファイルのインポートに失敗
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=squish,small,tiny
|
|||||||
|
|
||||||
home.changeMetadata.title=メタデータの変更
|
home.changeMetadata.title=メタデータの変更
|
||||||
home.changeMetadata.desc=PDFのメタデータを変更/削除/追加します。
|
home.changeMetadata.desc=PDFのメタデータを変更/削除/追加します。
|
||||||
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=ファイルをPDFに変換
|
home.fileToPDF.title=ファイルをPDFに変換
|
||||||
home.fileToPDF.desc=ほぼすべてのファイルをPDFに変換します。 (DOCX, PNG, XLS, PPT, TXTなど)
|
home.fileToPDF.desc=ほぼすべてのファイルをPDFに変換します。 (DOCX, PNG, XLS, PPT, TXTなど)
|
||||||
@@ -461,8 +471,8 @@ home.BookToPDF.title=PDFを書籍に変換
|
|||||||
home.BookToPDF.desc=calibreを使用してPDFを書籍/コミック形式に変換します
|
home.BookToPDF.desc=calibreを使用してPDFを書籍/コミック形式に変換します
|
||||||
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle
|
||||||
|
|
||||||
home.removeImagePdf.title=Remove image
|
home.removeImagePdf.title=画像の削除
|
||||||
home.removeImagePdf.desc=Remove image from PDF to reduce file size
|
home.removeImagePdf.desc=PDFから画像を削除してファイルサイズを小さくします
|
||||||
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
|
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
|
||||||
|
|
||||||
|
|
||||||
@@ -481,12 +491,14 @@ login.locked=あなたのアカウントはロックされています。
|
|||||||
login.signinTitle=サインインしてください
|
login.signinTitle=サインインしてください
|
||||||
login.ssoSignIn=シングルサインオンでログイン
|
login.ssoSignIn=シングルサインオンでログイン
|
||||||
login.oauth2AutoCreateDisabled=OAuth 2自動作成ユーザーが無効
|
login.oauth2AutoCreateDisabled=OAuth 2自動作成ユーザーが無効
|
||||||
|
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
|
||||||
login.oauth2RequestNotFound=認証リクエストが見つかりません
|
login.oauth2RequestNotFound=認証リクエストが見つかりません
|
||||||
login.oauth2InvalidUserInfoResponse=無効なユーザー情報の応答
|
login.oauth2InvalidUserInfoResponse=無効なユーザー情報の応答
|
||||||
login.oauth2invalidRequest=無効なリクエスト
|
login.oauth2invalidRequest=無効なリクエスト
|
||||||
login.oauth2AccessDenied=アクセス拒否
|
login.oauth2AccessDenied=アクセス拒否
|
||||||
login.oauth2InvalidTokenResponse=無効なトークン応答
|
login.oauth2InvalidTokenResponse=無効なトークン応答
|
||||||
login.oauth2InvalidIdToken=無効なIDトークン
|
login.oauth2InvalidIdToken=無効なIDトークン
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=最小輪郭面積:
|
|||||||
ScannerImageSplit.selectText.8=画像の最小の輪郭面積のしきい値を設定。
|
ScannerImageSplit.selectText.8=画像の最小の輪郭面積のしきい値を設定。
|
||||||
ScannerImageSplit.selectText.9=境界線サイズ:
|
ScannerImageSplit.selectText.9=境界線サイズ:
|
||||||
ScannerImageSplit.selectText.10=出力に白い縁取りが出ないように追加・削除される境界線の大きさを設定 (初期値:1)。
|
ScannerImageSplit.selectText.10=出力に白い縁取りが出ないように追加・削除される境界線の大きさを設定 (初期値:1)。
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=カラー
|
|||||||
pdfToImage.grey=グレースケール
|
pdfToImage.grey=グレースケール
|
||||||
pdfToImage.blackwhite=白黒 (データが失われる可能性があります!)
|
pdfToImage.blackwhite=白黒 (データが失われる可能性があります!)
|
||||||
pdfToImage.submit=変換
|
pdfToImage.submit=変換
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
@@ -1132,7 +1146,7 @@ error.discordSubmit=Discord - サポート投稿を提出
|
|||||||
|
|
||||||
|
|
||||||
#remove-image
|
#remove-image
|
||||||
removeImage.title=Remove image
|
removeImage.title=画像の削除
|
||||||
removeImage.header=Remove image
|
removeImage.header=画像の削除
|
||||||
removeImage.removeImage=Remove image
|
removeImage.removeImage=画像の削除
|
||||||
removeImage.submit=Remove image
|
removeImage.submit=画像を削除
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=사용자를 찾을 수 없습니다.
|
|||||||
incorrectPasswordMessage=현재 비밀번호가 틀립니다.
|
incorrectPasswordMessage=현재 비밀번호가 틀립니다.
|
||||||
usernameExistsMessage=새 사용자명이 이미 존재합니다.
|
usernameExistsMessage=새 사용자명이 이미 존재합니다.
|
||||||
invalidUsernameMessage=잘못된 사용자 이름입니다. 사용자 이름에는 문자, 숫자 및 다음 특수 문자(@._+-)만 포함할 수 있거나 유효한 이메일 주소여야 합니다.
|
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.
|
confirmPasswordErrorMessage=New Password and Confirm New Password must match.
|
||||||
deleteCurrentUserMessage=현재 로그인한 사용자를 삭제할 수 없습니다.
|
deleteCurrentUserMessage=현재 로그인한 사용자를 삭제할 수 없습니다.
|
||||||
deleteUsernameExistsMessage=사용자 이름이 존재하지 않으며 삭제할 수 없습니다.
|
deleteUsernameExistsMessage=사용자 이름이 존재하지 않으며 삭제할 수 없습니다.
|
||||||
downgradeCurrentUserMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다
|
downgradeCurrentUserMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다
|
||||||
|
disabledCurrentUserMessage=The current user cannot be disabled
|
||||||
downgradeCurrentUserLongMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다. 따라서 현재 사용자는 표시되지 않습니다.
|
downgradeCurrentUserLongMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다. 따라서 현재 사용자는 표시되지 않습니다.
|
||||||
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
|
||||||
userAlreadyExistsWebMessage=The user already exists as an web user.
|
userAlreadyExistsWebMessage=The user already exists as an web user.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=사용자
|
|||||||
adminUserSettings.addUser=새 사용자 추가
|
adminUserSettings.addUser=새 사용자 추가
|
||||||
adminUserSettings.deleteUser=Delete User
|
adminUserSettings.deleteUser=Delete User
|
||||||
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
adminUserSettings.confirmDeleteUser=Should the user be deleted?
|
||||||
|
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||||
adminUserSettings.usernameInfo=사용자 이름은 문자, 숫자, 특수 문자 @._+-만 포함할 수 있으며 유효한 이메일 주소여야 합니다.
|
adminUserSettings.usernameInfo=사용자 이름은 문자, 숫자, 특수 문자 @._+-만 포함할 수 있으며 유효한 이메일 주소여야 합니다.
|
||||||
adminUserSettings.roles=역할
|
adminUserSettings.roles=역할
|
||||||
adminUserSettings.role=역할
|
adminUserSettings.role=역할
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=다음 로그인 때 사용자명과 비밀번호
|
|||||||
adminUserSettings.submit=사용자 저장
|
adminUserSettings.submit=사용자 저장
|
||||||
adminUserSettings.changeUserRole=사용자의 역할 변경
|
adminUserSettings.changeUserRole=사용자의 역할 변경
|
||||||
adminUserSettings.authenticated=Authenticated
|
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
|
database.title=Database Import/Export
|
||||||
@@ -281,7 +291,7 @@ compressPdfs.tags=squish,small,tiny
|
|||||||
|
|
||||||
home.changeMetadata.title=메타데이터 변경
|
home.changeMetadata.title=메타데이터 변경
|
||||||
home.changeMetadata.desc=PDF 문서의 메타데이터를 수정/제거/추가합니다.
|
home.changeMetadata.desc=PDF 문서의 메타데이터를 수정/제거/추가합니다.
|
||||||
changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats
|
changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats
|
||||||
|
|
||||||
home.fileToPDF.title=파일을 PDF로 변환
|
home.fileToPDF.title=파일을 PDF로 변환
|
||||||
home.fileToPDF.desc=거의 모든 파일을 PDF로 변환합니다(DOCX, PNG, XLS, PPT, TXT 등)
|
home.fileToPDF.desc=거의 모든 파일을 PDF로 변환합니다(DOCX, PNG, XLS, PPT, TXT 등)
|
||||||
@@ -481,12 +491,14 @@ login.locked=계정이 잠겼습니다.
|
|||||||
login.signinTitle=로그인해 주세요.
|
login.signinTitle=로그인해 주세요.
|
||||||
login.ssoSignIn=싱글사인온을 통한 로그인
|
login.ssoSignIn=싱글사인온을 통한 로그인
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 사용자 자동 생성 비활성화됨
|
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.oauth2RequestNotFound=Authorization request not found
|
||||||
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
login.oauth2InvalidUserInfoResponse=Invalid User Info Response
|
||||||
login.oauth2invalidRequest=Invalid Request
|
login.oauth2invalidRequest=Invalid Request
|
||||||
login.oauth2AccessDenied=Access Denied
|
login.oauth2AccessDenied=Access Denied
|
||||||
login.oauth2InvalidTokenResponse=Invalid Token Response
|
login.oauth2InvalidTokenResponse=Invalid Token Response
|
||||||
login.oauth2InvalidIdToken=Invalid Id Token
|
login.oauth2InvalidIdToken=Invalid Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=최소 윤곽 영역:
|
|||||||
ScannerImageSplit.selectText.8=사진의 최소 윤곽선 영역 임계값을 설정합니다.
|
ScannerImageSplit.selectText.8=사진의 최소 윤곽선 영역 임계값을 설정합니다.
|
||||||
ScannerImageSplit.selectText.9=테두리 크기:
|
ScannerImageSplit.selectText.9=테두리 크기:
|
||||||
ScannerImageSplit.selectText.10=출력에서 흰색 테두리를 방지하기 위해 추가 및 제거되는 테두리의 크기를 설정합니다(기본값: 1).
|
ScannerImageSplit.selectText.10=출력에서 흰색 테두리를 방지하기 위해 추가 및 제거되는 테두리의 크기를 설정합니다(기본값: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=컬러
|
|||||||
pdfToImage.grey=그레이스케일
|
pdfToImage.grey=그레이스케일
|
||||||
pdfToImage.blackwhite=흑백 (데이터 손실 가능성 있음!)
|
pdfToImage.blackwhite=흑백 (데이터 손실 가능성 있음!)
|
||||||
pdfToImage.submit=변환
|
pdfToImage.submit=변환
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Gebruiker niet gevonden.
|
|||||||
incorrectPasswordMessage=Huidige wachtwoord is onjuist.
|
incorrectPasswordMessage=Huidige wachtwoord is onjuist.
|
||||||
usernameExistsMessage=Nieuwe gebruikersnaam bestaat al.
|
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.
|
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.
|
confirmPasswordErrorMessage=Nieuw wachtwoord en bevestig wachtwoord moeten overeenkomen.
|
||||||
deleteCurrentUserMessage=Kan niet een momenteel ingelogde gebruiker verwijderen.
|
deleteCurrentUserMessage=Kan niet een momenteel ingelogde gebruiker verwijderen.
|
||||||
deleteUsernameExistsMessage=De gebruikersnaam bestaat niet en kan niet verwijderd worden.
|
deleteUsernameExistsMessage=De gebruikersnaam bestaat niet en kan niet verwijderd worden.
|
||||||
downgradeCurrentUserMessage=Kan de rol van de huidige gebruiker niet downgraden
|
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.
|
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.
|
userAlreadyExistsOAuthMessage=De gebruiker bestaat al als een OAuth2 gebruiker.
|
||||||
userAlreadyExistsWebMessage=De gebruiker bestaat al als een web gebruiker.
|
userAlreadyExistsWebMessage=De gebruiker bestaat al als een web gebruiker.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Gebruiker
|
|||||||
adminUserSettings.addUser=Voeg nieuwe gebruiker toe
|
adminUserSettings.addUser=Voeg nieuwe gebruiker toe
|
||||||
adminUserSettings.deleteUser=Verwijder gebruiker
|
adminUserSettings.deleteUser=Verwijder gebruiker
|
||||||
adminUserSettings.confirmDeleteUser=Moet deze gebruiker verwijderd worden?
|
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.usernameInfo=Gebruikersnaam kan alleen letters, nummers en de volgende speciale tekens @._+- bevatten of moet een geldig emailadres zijn.
|
||||||
adminUserSettings.roles=Rollen
|
adminUserSettings.roles=Rollen
|
||||||
adminUserSettings.role=Rol
|
adminUserSettings.role=Rol
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Forceer gebruiker om gebruikersnaam/wachtwoord te
|
|||||||
adminUserSettings.submit=Gebruiker opslaan
|
adminUserSettings.submit=Gebruiker opslaan
|
||||||
adminUserSettings.changeUserRole=De rol van de gebruiker wijzigen
|
adminUserSettings.changeUserRole=De rol van de gebruiker wijzigen
|
||||||
adminUserSettings.authenticated=Geauthenticeerd
|
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
|
database.title=Database Import/Export
|
||||||
@@ -481,12 +491,14 @@ login.locked=Je account is geblokkeerd.
|
|||||||
login.signinTitle=Gelieve in te loggen
|
login.signinTitle=Gelieve in te loggen
|
||||||
login.ssoSignIn=Inloggen via Single Sign-on
|
login.ssoSignIn=Inloggen via Single Sign-on
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Automatisch aanmaken gebruiker uitgeschakeld
|
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.oauth2RequestNotFound=Autorisatieverzoek niet gevonden
|
||||||
login.oauth2InvalidUserInfoResponse=Ongeldige reactie op gebruikersinfo
|
login.oauth2InvalidUserInfoResponse=Ongeldige reactie op gebruikersinfo
|
||||||
login.oauth2invalidRequest=Ongeldig verzoek
|
login.oauth2invalidRequest=Ongeldig verzoek
|
||||||
login.oauth2AccessDenied=Toegang geweigerd
|
login.oauth2AccessDenied=Toegang geweigerd
|
||||||
login.oauth2InvalidTokenResponse=Ongeldige tokenreactie
|
login.oauth2InvalidTokenResponse=Ongeldige tokenreactie
|
||||||
login.oauth2InvalidIdToken=Ongeldige ID token
|
login.oauth2InvalidIdToken=Ongeldige ID token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimum contour oppervlakte:
|
|||||||
ScannerImageSplit.selectText.8=Stelt de minimale contour oppervlakte drempel in voor een foto
|
ScannerImageSplit.selectText.8=Stelt de minimale contour oppervlakte drempel in voor een foto
|
||||||
ScannerImageSplit.selectText.9=Randgrootte:
|
ScannerImageSplit.selectText.9=Randgrootte:
|
||||||
ScannerImageSplit.selectText.10=Stelt de grootte van de toegevoegde en verwijderde rand in om witte randen in de uitvoer te voorkomen (standaard: 1).
|
ScannerImageSplit.selectText.10=Stelt de grootte van de toegevoegde en verwijderde rand in om witte randen in de uitvoer te voorkomen (standaard: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Kleur
|
|||||||
pdfToImage.grey=Grijstinten
|
pdfToImage.grey=Grijstinten
|
||||||
pdfToImage.blackwhite=Zwart en wit (kan data verliezen!)
|
pdfToImage.blackwhite=Zwart en wit (kan data verliezen!)
|
||||||
pdfToImage.submit=Omzetten
|
pdfToImage.submit=Omzetten
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Bruker ikke funnet.
|
|||||||
incorrectPasswordMessage=Nåværende passord er feil.
|
incorrectPasswordMessage=Nåværende passord er feil.
|
||||||
usernameExistsMessage=Det nye brukernavnet eksisterer allerede.
|
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.
|
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.
|
confirmPasswordErrorMessage=Nytt passord og Bekreft nytt passord må være like.
|
||||||
deleteCurrentUserMessage=Kan ikke slette den innloggede brukeren.
|
deleteCurrentUserMessage=Kan ikke slette den innloggede brukeren.
|
||||||
deleteUsernameExistsMessage=Brukernavnet eksisterer ikke og kan ikke slettes.
|
deleteUsernameExistsMessage=Brukernavnet eksisterer ikke og kan ikke slettes.
|
||||||
downgradeCurrentUserMessage=Kan ikke nedgradere den innloggede brukerens rolle.
|
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.
|
downgradeCurrentUserLongMessage=Kan ikke nedgradere den innloggede brukerens rolle. Derfor vil ikke den innloggede brukeren bli vist.
|
||||||
userAlreadyExistsOAuthMessage=Brukeren eksisterer allerede som en OAuth2-bruker.
|
userAlreadyExistsOAuthMessage=Brukeren eksisterer allerede som en OAuth2-bruker.
|
||||||
userAlreadyExistsWebMessage=Brukeren eksisterer allerede som en web-bruker.
|
userAlreadyExistsWebMessage=Brukeren eksisterer allerede som en web-bruker.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Bruker
|
|||||||
adminUserSettings.addUser=Legg til Ny Bruker
|
adminUserSettings.addUser=Legg til Ny Bruker
|
||||||
adminUserSettings.deleteUser=Slett Bruker
|
adminUserSettings.deleteUser=Slett Bruker
|
||||||
adminUserSettings.confirmDeleteUser=Skal brukeren slettes?
|
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.usernameInfo=Brukernavn kan bare inneholde bokstaver, tall og følgende spesialtegn @._+- eller må være en gyldig e-postadresse.
|
||||||
adminUserSettings.roles=Roller
|
adminUserSettings.roles=Roller
|
||||||
adminUserSettings.role=Rolle
|
adminUserSettings.role=Rolle
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Tving bruker til å endre passord ved innlogging
|
|||||||
adminUserSettings.submit=Lagre Bruker
|
adminUserSettings.submit=Lagre Bruker
|
||||||
adminUserSettings.changeUserRole=Endre Brukerens Rolle
|
adminUserSettings.changeUserRole=Endre Brukerens Rolle
|
||||||
adminUserSettings.authenticated=Autentisert
|
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.title=Database Import/Eksport
|
||||||
@@ -481,12 +491,14 @@ login.locked=Kontoen din har blitt låst.
|
|||||||
login.signinTitle=Vennligst logg inn
|
login.signinTitle=Vennligst logg inn
|
||||||
login.ssoSignIn=Logg inn via Enkel Pålogging
|
login.ssoSignIn=Logg inn via Enkel Pålogging
|
||||||
login.oauth2AutoCreateDisabled=OAUTH2 Auto-Opretting av bruker deaktivert
|
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.oauth2RequestNotFound=Autentiseringsforespørsel ikke funnet
|
||||||
login.oauth2InvalidUserInfoResponse=Ugyldig brukerinforespons
|
login.oauth2InvalidUserInfoResponse=Ugyldig brukerinforespons
|
||||||
login.oauth2invalidRequest=Ugyldig forespørsel
|
login.oauth2invalidRequest=Ugyldig forespørsel
|
||||||
login.oauth2AccessDenied=Tilgang nektet
|
login.oauth2AccessDenied=Tilgang nektet
|
||||||
login.oauth2InvalidTokenResponse=Ugyldig tokenrespons
|
login.oauth2InvalidTokenResponse=Ugyldig tokenrespons
|
||||||
login.oauth2InvalidIdToken=Ugyldig Id Token
|
login.oauth2InvalidIdToken=Ugyldig Id Token
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimumskonturområde:
|
|||||||
ScannerImageSplit.selectText.8=Angir minimumskonturområde terskel for et bilde
|
ScannerImageSplit.selectText.8=Angir minimumskonturområde terskel for et bilde
|
||||||
ScannerImageSplit.selectText.9=Kantstørrelse:
|
ScannerImageSplit.selectText.9=Kantstørrelse:
|
||||||
ScannerImageSplit.selectText.10=Angir størrelsen på kanten som legges til og fjernes for å forhindre hvite kanter i utdataen (standard: 1).
|
ScannerImageSplit.selectText.10=Angir størrelsen på kanten som legges til og fjernes for å forhindre hvite kanter i utdataen (standard: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Farge
|
|||||||
pdfToImage.grey=Gråtone
|
pdfToImage.grey=Gråtone
|
||||||
pdfToImage.blackwhite=Svart-hvitt (kan miste data!)
|
pdfToImage.blackwhite=Svart-hvitt (kan miste data!)
|
||||||
pdfToImage.submit=Konverter
|
pdfToImage.submit=Konverter
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ userNotFoundMessage=Brak użytkownika.
|
|||||||
incorrectPasswordMessage=Nieprawidłowe hasło.
|
incorrectPasswordMessage=Nieprawidłowe hasło.
|
||||||
usernameExistsMessage=Taki uzytkownik już istnieje.
|
usernameExistsMessage=Taki uzytkownik już istnieje.
|
||||||
invalidUsernameMessage=Niewłaściwa nazwa użytkownika - musi zawierać litery, cyfry i @._+- LUB być adresem email.
|
invalidUsernameMessage=Niewłaściwa nazwa użytkownika - musi zawierać litery, cyfry i @._+- LUB być adresem email.
|
||||||
|
invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end.
|
||||||
confirmPasswordErrorMessage=Wpisz poprawnie hasło w OBA pola.
|
confirmPasswordErrorMessage=Wpisz poprawnie hasło w OBA pola.
|
||||||
deleteCurrentUserMessage=Nie można usunąć zalogowanego użytkownika
|
deleteCurrentUserMessage=Nie można usunąć zalogowanego użytkownika
|
||||||
deleteUsernameExistsMessage=Nie można usunąć zalogowanego użytkownika
|
deleteUsernameExistsMessage=Nie można usunąć zalogowanego użytkownika
|
||||||
downgradeCurrentUserMessage=Nie można obniżyć roli bieżącego użytkownika
|
downgradeCurrentUserMessage=Nie można obniżyć roli bieżącego użytkownika
|
||||||
|
disabledCurrentUserMessage=The current user cannot be disabled
|
||||||
downgradeCurrentUserLongMessage=Nie można obniżyć roli bieżącego użytkownika. W związku z tym bieżący użytkownik nie zostanie wyświetlony.
|
downgradeCurrentUserLongMessage=Nie można obniżyć roli bieżącego użytkownika. W związku z tym bieżący użytkownik nie zostanie wyświetlony.
|
||||||
userAlreadyExistsOAuthMessage=Takie konto użytkownika istnieje - stworzone za pomocą OAuth2.
|
userAlreadyExistsOAuthMessage=Takie konto użytkownika istnieje - stworzone za pomocą OAuth2.
|
||||||
userAlreadyExistsWebMessage=Takie konto użytkownika istnieje - stworzone za pomocą przeglądarki.
|
userAlreadyExistsWebMessage=Takie konto użytkownika istnieje - stworzone za pomocą przeglądarki.
|
||||||
@@ -177,6 +179,7 @@ adminUserSettings.user=Użytkownik
|
|||||||
adminUserSettings.addUser=Dodaj nowego użytkownika
|
adminUserSettings.addUser=Dodaj nowego użytkownika
|
||||||
adminUserSettings.deleteUser=Usuń użytkownika
|
adminUserSettings.deleteUser=Usuń użytkownika
|
||||||
adminUserSettings.confirmDeleteUser=Czy na pewno usunąć użytkownika?
|
adminUserSettings.confirmDeleteUser=Czy na pewno usunąć użytkownika?
|
||||||
|
adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled?
|
||||||
adminUserSettings.usernameInfo=Niewłaściwa nazwa użytkownika - musi zawierać litery, cyfry i @._+- LUB być adresem email.
|
adminUserSettings.usernameInfo=Niewłaściwa nazwa użytkownika - musi zawierać litery, cyfry i @._+- LUB być adresem email.
|
||||||
adminUserSettings.roles=Role
|
adminUserSettings.roles=Role
|
||||||
adminUserSettings.role=Rola
|
adminUserSettings.role=Rola
|
||||||
@@ -190,6 +193,13 @@ adminUserSettings.forceChange=Wymuś zmianę hasło po zalogowaniu
|
|||||||
adminUserSettings.submit=Zapisz użytkownika
|
adminUserSettings.submit=Zapisz użytkownika
|
||||||
adminUserSettings.changeUserRole=Zmień rolę użytkownika
|
adminUserSettings.changeUserRole=Zmień rolę użytkownika
|
||||||
adminUserSettings.authenticated=Zalogowany
|
adminUserSettings.authenticated=Zalogowany
|
||||||
|
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=Import/Eksport bazy danych
|
database.title=Import/Eksport bazy danych
|
||||||
@@ -481,12 +491,14 @@ login.locked=Konto jest zablokowane
|
|||||||
login.signinTitle=Zaloguj się
|
login.signinTitle=Zaloguj się
|
||||||
login.ssoSignIn=Zaloguj się za pomocą logowania jednokrotnego
|
login.ssoSignIn=Zaloguj się za pomocą logowania jednokrotnego
|
||||||
login.oauth2AutoCreateDisabled=Wyłączono automatyczne tworzenie użytkownika OAUTH2
|
login.oauth2AutoCreateDisabled=Wyłączono automatyczne tworzenie użytkownika OAUTH2
|
||||||
|
login.oauth2AdminBlockedUser=Registration or logging in of non-registered users is currently blocked. Please contact the administrator.
|
||||||
login.oauth2RequestNotFound=Błąd logowania OAuth2
|
login.oauth2RequestNotFound=Błąd logowania OAuth2
|
||||||
login.oauth2InvalidUserInfoResponse=Niewłaściwe dane logowania
|
login.oauth2InvalidUserInfoResponse=Niewłaściwe dane logowania
|
||||||
login.oauth2invalidRequest=Nieprawidłowe żądanie
|
login.oauth2invalidRequest=Nieprawidłowe żądanie
|
||||||
login.oauth2AccessDenied=Brak dostępu
|
login.oauth2AccessDenied=Brak dostępu
|
||||||
login.oauth2InvalidTokenResponse=Nieprawidłowa odpowiedź na token
|
login.oauth2InvalidTokenResponse=Nieprawidłowa odpowiedź na token
|
||||||
login.oauth2InvalidIdToken=Nieprawidłowa wartość tokenu
|
login.oauth2InvalidIdToken=Nieprawidłowa wartość tokenu
|
||||||
|
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
||||||
|
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
@@ -763,6 +775,7 @@ ScannerImageSplit.selectText.7=Minimalny obszar konturu:
|
|||||||
ScannerImageSplit.selectText.8=Ustawia próg minimalnego obszaru konturu dla zdjęcia
|
ScannerImageSplit.selectText.8=Ustawia próg minimalnego obszaru konturu dla zdjęcia
|
||||||
ScannerImageSplit.selectText.9=Rozmiar obramowania:
|
ScannerImageSplit.selectText.9=Rozmiar obramowania:
|
||||||
ScannerImageSplit.selectText.10=Ustawia rozmiar dodawanego i usuwanego obramowania, aby uniknąć białych obramowań na wyjściu (domyślnie: 1).
|
ScannerImageSplit.selectText.10=Ustawia rozmiar dodawanego i usuwanego obramowania, aby uniknąć białych obramowań na wyjściu (domyślnie: 1).
|
||||||
|
ScannerImageSplit.info=Python is not installed. It is required to run.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@@ -913,6 +926,7 @@ pdfToImage.color=Kolor
|
|||||||
pdfToImage.grey=Odcień szarości
|
pdfToImage.grey=Odcień szarości
|
||||||
pdfToImage.blackwhite=Czarno-biały (może spowodować utratę danych!)
|
pdfToImage.blackwhite=Czarno-biały (może spowodować utratę danych!)
|
||||||
pdfToImage.submit=Konwertuj
|
pdfToImage.submit=Konwertuj
|
||||||
|
pdfToImage.info=Python is not installed. Required for WebP conversion.
|
||||||
|
|
||||||
|
|
||||||
#addPassword
|
#addPassword
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user