mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-06 18:30:57 +00:00
main -> branch
This commit is contained in:
parent
1b6ce2a3e7
commit
b6c6a3445c
2
.github/workflows/scorecards.yml
vendored
2
.github/workflows/scorecards.yml
vendored
@ -74,6 +74,6 @@ jobs:
|
|||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@5f8171a638ada777af81d42b55959a643bb29017 # v3.28.12
|
uses: github/codeql-action/upload-sarif@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
17
Dockerfile
17
Dockerfile
@ -25,20 +25,16 @@ LABEL org.opencontainers.image.keywords="PDF, manipulation, merge, split, conver
|
|||||||
# Set Environment Variables
|
# Set Environment Variables
|
||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
VERSION_TAG=$VERSION_TAG \
|
VERSION_TAG=$VERSION_TAG \
|
||||||
JAVA_TOOL_OPTIONS="-XX:+UnlockExperimentalVMOptions \
|
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||||
-XX:MaxRAMPercentage=75 \
|
JAVA_CUSTOM_OPTS="" \
|
||||||
-XX:InitiatingHeapOccupancyPercent=20 \
|
|
||||||
-XX:+G1PeriodicGCInvokesConcurrent \
|
|
||||||
-XX:G1PeriodicGCInterval=10000 \
|
|
||||||
-XX:+UseStringDeduplication \
|
|
||||||
-XX:G1PeriodicGCSystemLoadThreshold=70" \
|
|
||||||
HOME=/home/stirlingpdfuser \
|
HOME=/home/stirlingpdfuser \
|
||||||
PUID=1000 \
|
PUID=1000 \
|
||||||
PGID=1000 \
|
PGID=1000 \
|
||||||
UMASK=022 \
|
UMASK=022 \
|
||||||
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
|
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
|
||||||
UNO_PATH=/usr/lib/libreoffice/program \
|
UNO_PATH=/usr/lib/libreoffice/program \
|
||||||
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc
|
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
|
||||||
|
PATH=$PATH:/opt/venv/bin
|
||||||
|
|
||||||
|
|
||||||
# JDK for app
|
# JDK for app
|
||||||
@ -77,9 +73,8 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
|
|||||||
py3-pillow@testing \
|
py3-pillow@testing \
|
||||||
py3-pdf2image@testing && \
|
py3-pdf2image@testing && \
|
||||||
python3 -m venv /opt/venv && \
|
python3 -m venv /opt/venv && \
|
||||||
export PATH="/opt/venv/bin:$PATH" && \
|
/opt/venv/bin/pip install --upgrade pip && \
|
||||||
pip install --upgrade pip && \
|
/opt/venv/bin/pip install --no-cache-dir --upgrade unoserver weasyprint && \
|
||||||
pip install --no-cache-dir --upgrade unoserver weasyprint && \
|
|
||||||
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
|
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||||
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
|
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||||
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
|
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
|
||||||
|
@ -32,13 +32,8 @@ ARG VERSION_TAG
|
|||||||
# Set Environment Variables
|
# Set Environment Variables
|
||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
VERSION_TAG=$VERSION_TAG \
|
VERSION_TAG=$VERSION_TAG \
|
||||||
JAVA_TOOL_OPTIONS="-XX:+UnlockExperimentalVMOptions \
|
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||||
-XX:MaxRAMPercentage=75 \
|
JAVA_CUSTOM_OPTS="" \
|
||||||
-XX:InitiatingHeapOccupancyPercent=20 \
|
|
||||||
-XX:+G1PeriodicGCInvokesConcurrent \
|
|
||||||
-XX:G1PeriodicGCInterval=10000 \
|
|
||||||
-XX:+UseStringDeduplication \
|
|
||||||
-XX:G1PeriodicGCSystemLoadThreshold=70" \
|
|
||||||
HOME=/home/stirlingpdfuser \
|
HOME=/home/stirlingpdfuser \
|
||||||
PUID=1000 \
|
PUID=1000 \
|
||||||
PGID=1000 \
|
PGID=1000 \
|
||||||
@ -47,7 +42,8 @@ ENV DOCKER_ENABLE_SECURITY=false \
|
|||||||
INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false \
|
INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false \
|
||||||
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
|
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
|
||||||
UNO_PATH=/usr/lib/libreoffice/program \
|
UNO_PATH=/usr/lib/libreoffice/program \
|
||||||
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc
|
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
|
||||||
|
PATH=$PATH:/opt/venv/bin
|
||||||
|
|
||||||
|
|
||||||
# JDK for app
|
# JDK for app
|
||||||
@ -87,9 +83,8 @@ RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/a
|
|||||||
py3-pillow@testing \
|
py3-pillow@testing \
|
||||||
py3-pdf2image@testing && \
|
py3-pdf2image@testing && \
|
||||||
python3 -m venv /opt/venv && \
|
python3 -m venv /opt/venv && \
|
||||||
export PATH="/opt/venv/bin:$PATH" && \
|
/opt/venv/bin/pip install --upgrade pip && \
|
||||||
pip install --upgrade pip && \
|
/opt/venv/bin/pip install --no-cache-dir --upgrade unoserver weasyprint && \
|
||||||
pip install --no-cache-dir --upgrade unoserver weasyprint && \
|
|
||||||
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
|
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||||
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
|
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||||
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
|
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
|
||||||
|
@ -7,13 +7,8 @@ ARG VERSION_TAG
|
|||||||
ENV DOCKER_ENABLE_SECURITY=false \
|
ENV DOCKER_ENABLE_SECURITY=false \
|
||||||
HOME=/home/stirlingpdfuser \
|
HOME=/home/stirlingpdfuser \
|
||||||
VERSION_TAG=$VERSION_TAG \
|
VERSION_TAG=$VERSION_TAG \
|
||||||
JAVA_TOOL_OPTIONS="-XX:+UnlockExperimentalVMOptions \
|
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||||
-XX:MaxRAMPercentage=75 \
|
JAVA_CUSTOM_OPTS="" \
|
||||||
-XX:InitiatingHeapOccupancyPercent=20 \
|
|
||||||
-XX:+G1PeriodicGCInvokesConcurrent \
|
|
||||||
-XX:G1PeriodicGCInterval=10000 \
|
|
||||||
-XX:+UseStringDeduplication \
|
|
||||||
-XX:G1PeriodicGCSystemLoadThreshold=70" \
|
|
||||||
PUID=1000 \
|
PUID=1000 \
|
||||||
PGID=1000 \
|
PGID=1000 \
|
||||||
UMASK=022
|
UMASK=022
|
||||||
|
76
README.md
76
README.md
@ -116,46 +116,46 @@ Stirling-PDF currently supports 39 languages!
|
|||||||
|
|
||||||
| Language | Progress |
|
| Language | Progress |
|
||||||
| -------------------------------------------- | -------------------------------------- |
|
| -------------------------------------------- | -------------------------------------- |
|
||||||
| Arabic (العربية) (ar_AR) |  |
|
| Arabic (العربية) (ar_AR) |  |
|
||||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||||
| 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) |  |
|
||||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
| Indonesian (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) |  |
|
||||||
| Persian (فارسی) (fa_IR) |  |
|
| Persian (فارسی) (fa_IR) |  |
|
||||||
| 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) |  |
|
||||||
| Serbian 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) |  |
|
||||||
| Slovenian (Slovenščina) (sl_SI) |  |
|
| Slovenian (Slovenščina) (sl_SI) |  |
|
||||||
| Spanish (Español) (es_ES) |  |
|
| Spanish (Español) (es_ES) |  |
|
||||||
| Swedish (Svenska) (sv_SE) |  |
|
| Swedish (Svenska) (sv_SE) |  |
|
||||||
| Thai (ไทย) (th_TH) |  |
|
| Thai (ไทย) (th_TH) |  |
|
||||||
| Tibetan (བོད་ཡིག་) (zh_BO) |  |
|
| Tibetan (བོད་ཡིག་) (zh_BO) |  |
|
||||||
| 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) |  |
|
||||||
|
|
||||||
|
|
||||||
## Stirling PDF Enterprise
|
## Stirling PDF Enterprise
|
||||||
|
@ -25,7 +25,7 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "stirling.software"
|
group = "stirling.software"
|
||||||
version = "0.44.3"
|
version = "0.45.0"
|
||||||
|
|
||||||
java {
|
java {
|
||||||
// 17 is lowest but we support and recommend 21
|
// 17 is lowest but we support and recommend 21
|
||||||
@ -333,6 +333,10 @@ dependencies {
|
|||||||
|
|
||||||
|
|
||||||
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") {
|
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") {
|
||||||
|
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||||
|
implementation 'io.micrometer:micrometer-registry-prometheus'
|
||||||
|
|
||||||
implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
|
||||||
implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE"
|
implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE"
|
||||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
stirling-pdf:
|
stirling-pdf:
|
||||||
container_name: Stirling-PDF-Security-Fat-with-login
|
container_name: Stirling-PDF-Security-Fat-with-login
|
||||||
image: stirlingtools/stirling-pdf:latest-fat
|
image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest-fat
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
export JAVA_TOOL_OPTIONS="${JAVA_BASE_OPTS} ${JAVA_CUSTOM_OPTS}"
|
||||||
|
echo "running with JAVA_TOOL_OPTIONS ${JAVA_BASE_OPTS} ${JAVA_CUSTOM_OPTS}"
|
||||||
|
|
||||||
# Update the user and group IDs as per environment variables
|
# Update the user and group IDs as per environment variables
|
||||||
if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then
|
if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then
|
||||||
usermod -o -u "$PUID" stirlingpdfuser || true
|
usermod -o -u "$PUID" stirlingpdfuser || true
|
||||||
|
@ -8,6 +8,8 @@ import org.springframework.core.annotation.Order;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.EnterpriseEdition;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties.Premium;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
@ -22,6 +24,7 @@ public class EEAppConfig {
|
|||||||
ApplicationProperties applicationProperties, LicenseKeyChecker licenseKeyChecker) {
|
ApplicationProperties applicationProperties, LicenseKeyChecker licenseKeyChecker) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.licenseKeyChecker = licenseKeyChecker;
|
this.licenseKeyChecker = licenseKeyChecker;
|
||||||
|
migrateEnterpriseSettingsToPremium(this.applicationProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "runningEE")
|
@Bean(name = "runningEE")
|
||||||
@ -31,6 +34,74 @@ public class EEAppConfig {
|
|||||||
|
|
||||||
@Bean(name = "SSOAutoLogin")
|
@Bean(name = "SSOAutoLogin")
|
||||||
public boolean ssoAutoLogin() {
|
public boolean ssoAutoLogin() {
|
||||||
return applicationProperties.getEnterpriseEdition().isSsoAutoLogin();
|
return applicationProperties.getPremium().getProFeatures().isSsoAutoLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove post migration
|
||||||
|
public void migrateEnterpriseSettingsToPremium(ApplicationProperties applicationProperties) {
|
||||||
|
EnterpriseEdition enterpriseEdition = applicationProperties.getEnterpriseEdition();
|
||||||
|
Premium premium = applicationProperties.getPremium();
|
||||||
|
|
||||||
|
// Only proceed if both objects exist
|
||||||
|
if (enterpriseEdition == null || premium == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the license key if it's set in enterprise but not in premium
|
||||||
|
if (premium.getKey() == null
|
||||||
|
|| premium.getKey().equals("00000000-0000-0000-0000-000000000000")) {
|
||||||
|
if (enterpriseEdition.getKey() != null
|
||||||
|
&& !enterpriseEdition.getKey().equals("00000000-0000-0000-0000-000000000000")) {
|
||||||
|
premium.setKey(enterpriseEdition.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy enabled state if enterprise is enabled but premium is not
|
||||||
|
if (!premium.isEnabled() && enterpriseEdition.isEnabled()) {
|
||||||
|
premium.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy SSO auto login setting
|
||||||
|
if (!premium.getProFeatures().isSsoAutoLogin() && enterpriseEdition.isSsoAutoLogin()) {
|
||||||
|
premium.getProFeatures().setSsoAutoLogin(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy CustomMetadata settings
|
||||||
|
Premium.ProFeatures.CustomMetadata premiumMetadata =
|
||||||
|
premium.getProFeatures().getCustomMetadata();
|
||||||
|
EnterpriseEdition.CustomMetadata enterpriseMetadata = enterpriseEdition.getCustomMetadata();
|
||||||
|
|
||||||
|
if (enterpriseMetadata != null && premiumMetadata != null) {
|
||||||
|
// Copy autoUpdateMetadata setting
|
||||||
|
if (!premiumMetadata.isAutoUpdateMetadata()
|
||||||
|
&& enterpriseMetadata.isAutoUpdateMetadata()) {
|
||||||
|
premiumMetadata.setAutoUpdateMetadata(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy author if not set in premium but set in enterprise
|
||||||
|
if ((premiumMetadata.getAuthor() == null
|
||||||
|
|| premiumMetadata.getAuthor().trim().isEmpty()
|
||||||
|
|| "username".equals(premiumMetadata.getAuthor()))
|
||||||
|
&& enterpriseMetadata.getAuthor() != null
|
||||||
|
&& !enterpriseMetadata.getAuthor().trim().isEmpty()) {
|
||||||
|
premiumMetadata.setAuthor(enterpriseMetadata.getAuthor());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy creator if not set in premium but set in enterprise and different from default
|
||||||
|
if ((premiumMetadata.getCreator() == null
|
||||||
|
|| "Stirling-PDF".equals(premiumMetadata.getCreator()))
|
||||||
|
&& enterpriseMetadata.getCreator() != null
|
||||||
|
&& !"Stirling-PDF".equals(enterpriseMetadata.getCreator())) {
|
||||||
|
premiumMetadata.setCreator(enterpriseMetadata.getCreator());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy producer if not set in premium but set in enterprise and different from default
|
||||||
|
if ((premiumMetadata.getProducer() == null
|
||||||
|
|| "Stirling-PDF".equals(premiumMetadata.getProducer()))
|
||||||
|
&& enterpriseMetadata.getProducer() != null
|
||||||
|
&& !"Stirling-PDF".equals(enterpriseMetadata.getProducer())) {
|
||||||
|
premiumMetadata.setProducer(enterpriseMetadata.getProducer());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,17 @@ import java.net.URI;
|
|||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
|
||||||
|
import org.bouncycastle.crypto.signers.Ed25519Signer;
|
||||||
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
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 com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.posthog.java.shaded.org.json.JSONException;
|
||||||
import com.posthog.java.shaded.org.json.JSONObject;
|
import com.posthog.java.shaded.org.json.JSONObject;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -20,11 +25,19 @@ import stirling.software.SPDF.utils.GeneralUtils;
|
|||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class KeygenLicenseVerifier {
|
public class KeygenLicenseVerifier {
|
||||||
// todo: place in config files?
|
// License verification configuration
|
||||||
private static final String ACCOUNT_ID = "e5430f69-e834-4ae4-befd-b602aae5f372";
|
private static final String ACCOUNT_ID = "e5430f69-e834-4ae4-befd-b602aae5f372";
|
||||||
private static final String BASE_URL = "https://api.keygen.sh/v1/accounts";
|
private static final String BASE_URL = "https://api.keygen.sh/v1/accounts";
|
||||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
|
|
||||||
|
private static final String PUBLIC_KEY =
|
||||||
|
"9fbc0d78593dcfcf03c945146edd60083bf5fae77dbc08aaa3935f03ce94a58d";
|
||||||
|
|
||||||
|
private static final String CERT_PREFIX = "-----BEGIN LICENSE FILE-----";
|
||||||
|
private static final String CERT_SUFFIX = "-----END LICENSE FILE-----";
|
||||||
|
|
||||||
|
private static final String JWT_PREFIX = "key/";
|
||||||
|
|
||||||
|
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@ -32,9 +45,367 @@ public class KeygenLicenseVerifier {
|
|||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean verifyLicense(String licenseKey) {
|
public boolean verifyLicense(String licenseKeyOrCert) {
|
||||||
|
if (isCertificateLicense(licenseKeyOrCert)) {
|
||||||
|
log.info("Detected certificate-based license. Processing...");
|
||||||
|
return verifyCertificateLicense(licenseKeyOrCert);
|
||||||
|
} else if (isJWTLicense(licenseKeyOrCert)) {
|
||||||
|
log.info("Detected JWT-style license key. Processing...");
|
||||||
|
return verifyJWTLicense(licenseKeyOrCert);
|
||||||
|
} else {
|
||||||
|
log.info("Detected standard license key. Processing...");
|
||||||
|
return verifyStandardLicense(licenseKeyOrCert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCertificateLicense(String license) {
|
||||||
|
return license != null && license.trim().startsWith(CERT_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isJWTLicense(String license) {
|
||||||
|
return license != null && license.trim().startsWith(JWT_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verifyCertificateLicense(String licenseFile) {
|
||||||
try {
|
try {
|
||||||
log.info("Checking license key");
|
log.info("Verifying certificate-based license");
|
||||||
|
|
||||||
|
String encodedPayload = licenseFile;
|
||||||
|
// Remove the header
|
||||||
|
encodedPayload = encodedPayload.replace(CERT_PREFIX, "");
|
||||||
|
// Remove the footer
|
||||||
|
encodedPayload = encodedPayload.replace(CERT_SUFFIX, "");
|
||||||
|
// Remove all newlines
|
||||||
|
encodedPayload = encodedPayload.replaceAll("\\r?\\n", "");
|
||||||
|
|
||||||
|
byte[] payloadBytes = Base64.getDecoder().decode(encodedPayload);
|
||||||
|
String payload = new String(payloadBytes);
|
||||||
|
|
||||||
|
log.info("Decoded certificate payload: {}", payload);
|
||||||
|
|
||||||
|
String encryptedData = "";
|
||||||
|
String encodedSignature = "";
|
||||||
|
String algorithm = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject attrs = new JSONObject(payload);
|
||||||
|
encryptedData = (String) attrs.get("enc");
|
||||||
|
encodedSignature = (String) attrs.get("sig");
|
||||||
|
algorithm = (String) attrs.get("alg");
|
||||||
|
|
||||||
|
log.info("Certificate algorithm: {}", algorithm);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
log.error("Failed to parse license file: {}", e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify license file algorithm
|
||||||
|
if (!algorithm.equals("base64+ed25519")) {
|
||||||
|
log.error(
|
||||||
|
"Unsupported algorithm: {}. Only base64+ed25519 is supported.", algorithm);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify signature
|
||||||
|
boolean isSignatureValid = verifyEd25519Signature(encryptedData, encodedSignature);
|
||||||
|
if (!isSignatureValid) {
|
||||||
|
log.error("License file signature is invalid");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("License file signature is valid");
|
||||||
|
|
||||||
|
// Decode the base64 data
|
||||||
|
String decodedData;
|
||||||
|
try {
|
||||||
|
decodedData = new String(Base64.getDecoder().decode(encryptedData));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
log.error("Failed to decode license data: {}", e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the certificate data
|
||||||
|
boolean isValid = processCertificateData(decodedData);
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error verifying certificate license: {}", e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verifyEd25519Signature(String encryptedData, String encodedSignature) {
|
||||||
|
try {
|
||||||
|
log.info("Signature to verify: {}", encodedSignature);
|
||||||
|
log.info("Public key being used: {}", PUBLIC_KEY);
|
||||||
|
|
||||||
|
byte[] signatureBytes = Base64.getDecoder().decode(encodedSignature);
|
||||||
|
|
||||||
|
// Create the signing data format - prefix with "license/"
|
||||||
|
String signingData = String.format("license/%s", encryptedData);
|
||||||
|
byte[] signingDataBytes = signingData.getBytes();
|
||||||
|
|
||||||
|
log.info("Signing data length: {} bytes", signingDataBytes.length);
|
||||||
|
|
||||||
|
byte[] publicKeyBytes = Hex.decode(PUBLIC_KEY);
|
||||||
|
|
||||||
|
Ed25519PublicKeyParameters verifierParams =
|
||||||
|
new Ed25519PublicKeyParameters(publicKeyBytes, 0);
|
||||||
|
Ed25519Signer verifier = new Ed25519Signer();
|
||||||
|
|
||||||
|
verifier.init(false, verifierParams);
|
||||||
|
verifier.update(signingDataBytes, 0, signingDataBytes.length);
|
||||||
|
|
||||||
|
// Verify the signature
|
||||||
|
boolean result = verifier.verifySignature(signatureBytes);
|
||||||
|
if (!result) {
|
||||||
|
log.error("Signature verification failed with standard public key");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error verifying Ed25519 signature: {}", e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean processCertificateData(String certData) {
|
||||||
|
try {
|
||||||
|
log.info("Processing certificate data: {}", certData);
|
||||||
|
|
||||||
|
JSONObject licenseData = new JSONObject(certData);
|
||||||
|
JSONObject metaObj = licenseData.optJSONObject("meta");
|
||||||
|
if (metaObj != null) {
|
||||||
|
String issuedStr = metaObj.optString("issued", null);
|
||||||
|
String expiryStr = metaObj.optString("expiry", null);
|
||||||
|
|
||||||
|
if (issuedStr != null && expiryStr != null) {
|
||||||
|
java.time.Instant issued = java.time.Instant.parse(issuedStr);
|
||||||
|
java.time.Instant expiry = java.time.Instant.parse(expiryStr);
|
||||||
|
java.time.Instant now = java.time.Instant.now();
|
||||||
|
|
||||||
|
if (issued.isAfter(now)) {
|
||||||
|
log.error(
|
||||||
|
"License file issued date is in the future. Please adjust system time or request a new license");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the license file has expired
|
||||||
|
if (expiry.isBefore(now)) {
|
||||||
|
log.error("License file has expired on {}", expiryStr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("License file valid until {}", expiryStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the main license data
|
||||||
|
JSONObject dataObj = licenseData.optJSONObject("data");
|
||||||
|
if (dataObj == null) {
|
||||||
|
log.error("No data object found in certificate");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract license or machine information
|
||||||
|
JSONObject attributesObj = dataObj.optJSONObject("attributes");
|
||||||
|
if (attributesObj != null) {
|
||||||
|
log.info("Found attributes in certificate data");
|
||||||
|
|
||||||
|
// Extract metadata
|
||||||
|
JSONObject metadataObj = attributesObj.optJSONObject("metadata");
|
||||||
|
if (metadataObj != null) {
|
||||||
|
int users = metadataObj.optInt("users", 0);
|
||||||
|
if (users > 0) {
|
||||||
|
applicationProperties.getPremium().setMaxUsers(users);
|
||||||
|
log.info("License allows for {} users", users);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check maxUsers directly in attributes if present from policy definition
|
||||||
|
// if (attributesObj.has("maxUsers")) {
|
||||||
|
// int maxUsers = attributesObj.optInt("maxUsers", 0);
|
||||||
|
// if (maxUsers > 0) {
|
||||||
|
// applicationProperties.getPremium().setMaxUsers(maxUsers);
|
||||||
|
// log.info("License directly specifies {} max users",
|
||||||
|
// maxUsers);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Check license status if available
|
||||||
|
String status = attributesObj.optString("status", null);
|
||||||
|
if (status != null
|
||||||
|
&& !status.equals("ACTIVE")
|
||||||
|
&& !status.equals("EXPIRING")) { // Accept "EXPIRING" status as valid
|
||||||
|
log.error("License status is not active: {}", status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error processing certificate data: {}", e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verifyJWTLicense(String licenseKey) {
|
||||||
|
try {
|
||||||
|
log.info("Verifying ED25519_SIGN format license key");
|
||||||
|
|
||||||
|
// Remove the "key/" prefix
|
||||||
|
String licenseData = licenseKey.substring(JWT_PREFIX.length());
|
||||||
|
|
||||||
|
// Split into payload and signature
|
||||||
|
String[] parts = licenseData.split("\\.", 2);
|
||||||
|
if (parts.length != 2) {
|
||||||
|
log.error(
|
||||||
|
"Invalid ED25519_SIGN license format. Expected format: key/payload.signature");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String encodedPayload = parts[0];
|
||||||
|
String encodedSignature = parts[1];
|
||||||
|
|
||||||
|
// Verify signature
|
||||||
|
boolean isSignatureValid = verifyJWTSignature(encodedPayload, encodedSignature);
|
||||||
|
if (!isSignatureValid) {
|
||||||
|
log.error("ED25519_SIGN license signature is invalid");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("ED25519_SIGN license signature is valid");
|
||||||
|
|
||||||
|
// Decode and process payload - first convert from URL-safe base64 if needed
|
||||||
|
String base64Payload = encodedPayload.replace('-', '+').replace('_', '/');
|
||||||
|
byte[] payloadBytes = Base64.getDecoder().decode(base64Payload);
|
||||||
|
String payload = new String(payloadBytes);
|
||||||
|
|
||||||
|
// Process the license payload
|
||||||
|
boolean isValid = processJWTLicensePayload(payload);
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error verifying ED25519_SIGN license: {}", e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verifyJWTSignature(String encodedPayload, String encodedSignature) {
|
||||||
|
try {
|
||||||
|
// Decode base64 signature
|
||||||
|
byte[] signatureBytes =
|
||||||
|
Base64.getDecoder()
|
||||||
|
.decode(encodedSignature.replace('-', '+').replace('_', '/'));
|
||||||
|
|
||||||
|
// For ED25519_SIGN format, the signing data is "key/" + encodedPayload
|
||||||
|
String signingData = String.format("key/%s", encodedPayload);
|
||||||
|
byte[] dataBytes = signingData.getBytes();
|
||||||
|
|
||||||
|
byte[] publicKeyBytes = Hex.decode(PUBLIC_KEY);
|
||||||
|
Ed25519PublicKeyParameters verifierParams =
|
||||||
|
new Ed25519PublicKeyParameters(publicKeyBytes, 0);
|
||||||
|
Ed25519Signer verifier = new Ed25519Signer();
|
||||||
|
|
||||||
|
verifier.init(false, verifierParams);
|
||||||
|
verifier.update(dataBytes, 0, dataBytes.length);
|
||||||
|
|
||||||
|
// Verify the signature
|
||||||
|
return verifier.verifySignature(signatureBytes);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error verifying JWT signature: {}", e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean processJWTLicensePayload(String payload) {
|
||||||
|
try {
|
||||||
|
log.info("Processing license payload: {}", payload);
|
||||||
|
|
||||||
|
JSONObject licenseData = new JSONObject(payload);
|
||||||
|
|
||||||
|
JSONObject licenseObj = licenseData.optJSONObject("license");
|
||||||
|
if (licenseObj == null) {
|
||||||
|
String id = licenseData.optString("id", null);
|
||||||
|
if (id != null) {
|
||||||
|
log.info("Found license ID: {}", id);
|
||||||
|
licenseObj = licenseData; // Use the root object as the license object
|
||||||
|
} else {
|
||||||
|
log.error("License data not found in payload");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String licenseId = licenseObj.optString("id", "unknown");
|
||||||
|
log.info("Processing license with ID: {}", licenseId);
|
||||||
|
|
||||||
|
// Check expiry date
|
||||||
|
String expiryStr = licenseObj.optString("expiry", null);
|
||||||
|
if (expiryStr != null && !expiryStr.equals("null")) {
|
||||||
|
java.time.Instant expiry = java.time.Instant.parse(expiryStr);
|
||||||
|
java.time.Instant now = java.time.Instant.now();
|
||||||
|
|
||||||
|
if (now.isAfter(expiry)) {
|
||||||
|
log.error("License has expired on {}", expiryStr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("License valid until {}", expiryStr);
|
||||||
|
} else {
|
||||||
|
log.info("License has no expiration date");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract account, product, policy info
|
||||||
|
JSONObject accountObj = licenseData.optJSONObject("account");
|
||||||
|
if (accountObj != null) {
|
||||||
|
String accountId = accountObj.optString("id", "unknown");
|
||||||
|
log.info("License belongs to account: {}", accountId);
|
||||||
|
|
||||||
|
// Verify this matches your expected account ID
|
||||||
|
if (!ACCOUNT_ID.equals(accountId)) {
|
||||||
|
log.warn("License account ID does not match expected account ID");
|
||||||
|
// You might want to fail verification here depending on your requirements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract policy information if available
|
||||||
|
JSONObject policyObj = licenseData.optJSONObject("policy");
|
||||||
|
if (policyObj != null) {
|
||||||
|
String policyId = policyObj.optString("id", "unknown");
|
||||||
|
log.info("License uses policy: {}", policyId);
|
||||||
|
|
||||||
|
// Extract max users from policy if available (customize based on your policy
|
||||||
|
// structure)
|
||||||
|
int users = policyObj.optInt("users", 0);
|
||||||
|
if (users > 0) {
|
||||||
|
applicationProperties.getPremium().setMaxUsers(users);
|
||||||
|
log.info("License allows for {} users", users);
|
||||||
|
} else {
|
||||||
|
// Try to get users from metadata if present
|
||||||
|
Object metadataObj = policyObj.opt("metadata");
|
||||||
|
if (metadataObj instanceof JSONObject) {
|
||||||
|
JSONObject metadata = (JSONObject) metadataObj;
|
||||||
|
users = metadata.optInt("users", 1);
|
||||||
|
applicationProperties.getPremium().setMaxUsers(users);
|
||||||
|
log.info("License allows for {} users (from metadata)", users);
|
||||||
|
} else {
|
||||||
|
// Default value
|
||||||
|
applicationProperties.getPremium().setMaxUsers(1);
|
||||||
|
log.info("Using default of 1 user for license");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error processing license payload: {}", e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verifyStandardLicense(String licenseKey) {
|
||||||
|
try {
|
||||||
|
log.info("Checking standard license key");
|
||||||
String machineFingerprint = generateMachineFingerprint();
|
String machineFingerprint = generateMachineFingerprint();
|
||||||
|
|
||||||
// First, try to validate the license
|
// First, try to validate the license
|
||||||
@ -44,7 +415,7 @@ public class KeygenLicenseVerifier {
|
|||||||
String licenseId = validationResponse.path("data").path("id").asText();
|
String licenseId = validationResponse.path("data").path("id").asText();
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
String code = validationResponse.path("meta").path("code").asText();
|
String code = validationResponse.path("meta").path("code").asText();
|
||||||
log.debug(code);
|
log.info(code);
|
||||||
if ("NO_MACHINE".equals(code)
|
if ("NO_MACHINE".equals(code)
|
||||||
|| "NO_MACHINES".equals(code)
|
|| "NO_MACHINES".equals(code)
|
||||||
|| "FINGERPRINT_SCOPE_MISMATCH".equals(code)) {
|
|| "FINGERPRINT_SCOPE_MISMATCH".equals(code)) {
|
||||||
@ -69,7 +440,7 @@ public class KeygenLicenseVerifier {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error verifying license: {}", e.getMessage());
|
log.error("Error verifying standard license: {}", e.getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,7 +467,7 @@ public class KeygenLicenseVerifier {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
log.debug("ValidateLicenseResponse body: {}", response.body());
|
log.info("ValidateLicenseResponse body: {}", response.body());
|
||||||
JsonNode jsonResponse = objectMapper.readTree(response.body());
|
JsonNode jsonResponse = objectMapper.readTree(response.body());
|
||||||
if (response.statusCode() == 200) {
|
if (response.statusCode() == 200) {
|
||||||
JsonNode metaNode = jsonResponse.path("meta");
|
JsonNode metaNode = jsonResponse.path("meta");
|
||||||
@ -105,9 +476,9 @@ public class KeygenLicenseVerifier {
|
|||||||
String detail = metaNode.path("detail").asText();
|
String detail = metaNode.path("detail").asText();
|
||||||
String code = metaNode.path("code").asText();
|
String code = metaNode.path("code").asText();
|
||||||
|
|
||||||
log.debug("License validity: " + isValid);
|
log.info("License validity: " + isValid);
|
||||||
log.debug("Validation detail: " + detail);
|
log.info("Validation detail: " + detail);
|
||||||
log.debug("Validation code: " + code);
|
log.info("Validation code: " + code);
|
||||||
|
|
||||||
int users =
|
int users =
|
||||||
jsonResponse
|
jsonResponse
|
||||||
@ -116,7 +487,7 @@ public class KeygenLicenseVerifier {
|
|||||||
.path("metadata")
|
.path("metadata")
|
||||||
.path("users")
|
.path("users")
|
||||||
.asInt(0);
|
.asInt(0);
|
||||||
applicationProperties.getEnterpriseEdition().setMaxUsers(users);
|
applicationProperties.getPremium().setMaxUsers(users);
|
||||||
log.info(applicationProperties.toString());
|
log.info(applicationProperties.toString());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -148,13 +519,8 @@ public class KeygenLicenseVerifier {
|
|||||||
.put("fingerprint", machineFingerprint)
|
.put("fingerprint", machineFingerprint)
|
||||||
.put(
|
.put(
|
||||||
"platform",
|
"platform",
|
||||||
System.getProperty(
|
System.getProperty("os.name"))
|
||||||
"os.name")) // Added
|
.put("name", hostname))
|
||||||
// platform
|
|
||||||
// parameter
|
|
||||||
.put(
|
|
||||||
"name",
|
|
||||||
hostname)) // Added name parameter
|
|
||||||
.put(
|
.put(
|
||||||
"relationships",
|
"relationships",
|
||||||
new JSONObject()
|
new JSONObject()
|
||||||
@ -176,16 +542,12 @@ public class KeygenLicenseVerifier {
|
|||||||
.uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/machines"))
|
.uri(URI.create(BASE_URL + "/" + ACCOUNT_ID + "/machines"))
|
||||||
.header("Content-Type", "application/vnd.api+json")
|
.header("Content-Type", "application/vnd.api+json")
|
||||||
.header("Accept", "application/vnd.api+json")
|
.header("Accept", "application/vnd.api+json")
|
||||||
.header(
|
.header("Authorization", "License " + licenseKey)
|
||||||
"Authorization",
|
.POST(HttpRequest.BodyPublishers.ofString(body.toString()))
|
||||||
"License " + licenseKey) // Keep the license key authentication
|
|
||||||
.POST(
|
|
||||||
HttpRequest.BodyPublishers.ofString(
|
|
||||||
body.toString())) // Send the JSON body
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
log.debug("activateMachine Response body: " + response.body());
|
log.info("activateMachine Response body: " + response.body());
|
||||||
if (response.statusCode() == 201) {
|
if (response.statusCode() == 201) {
|
||||||
log.info("Machine activated successfully");
|
log.info("Machine activated successfully");
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package stirling.software.SPDF.EE;
|
package stirling.software.SPDF.EE;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
@ -15,11 +18,13 @@ import stirling.software.SPDF.utils.GeneralUtils;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class LicenseKeyChecker {
|
public class LicenseKeyChecker {
|
||||||
|
|
||||||
|
private static final String FILE_PREFIX = "file:";
|
||||||
|
|
||||||
private final KeygenLicenseVerifier licenseService;
|
private final KeygenLicenseVerifier licenseService;
|
||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
private boolean enterpriseEnabledResult = false;
|
private boolean premiumEnabledResult = false;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LicenseKeyChecker(
|
public LicenseKeyChecker(
|
||||||
@ -35,27 +40,58 @@ public class LicenseKeyChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkLicense() {
|
private void checkLicense() {
|
||||||
if (!applicationProperties.getEnterpriseEdition().isEnabled()) {
|
if (!applicationProperties.getPremium().isEnabled()) {
|
||||||
enterpriseEnabledResult = false;
|
premiumEnabledResult = false;
|
||||||
} else {
|
} else {
|
||||||
enterpriseEnabledResult =
|
String licenseKey = getLicenseKeyContent(applicationProperties.getPremium().getKey());
|
||||||
licenseService.verifyLicense(
|
if (licenseKey != null) {
|
||||||
applicationProperties.getEnterpriseEdition().getKey());
|
premiumEnabledResult = licenseService.verifyLicense(licenseKey);
|
||||||
if (enterpriseEnabledResult) {
|
if (premiumEnabledResult) {
|
||||||
log.info("License key is valid.");
|
log.info("License key is valid.");
|
||||||
|
} else {
|
||||||
|
log.info("License key is invalid.");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.info("License key is invalid.");
|
log.error("Failed to obtain license key content.");
|
||||||
|
premiumEnabledResult = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getLicenseKeyContent(String keyOrFilePath) {
|
||||||
|
if (keyOrFilePath == null || keyOrFilePath.trim().isEmpty()) {
|
||||||
|
log.error("License key is not specified");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's a file reference
|
||||||
|
if (keyOrFilePath.startsWith(FILE_PREFIX)) {
|
||||||
|
String filePath = keyOrFilePath.substring(FILE_PREFIX.length());
|
||||||
|
try {
|
||||||
|
Path path = Paths.get(filePath);
|
||||||
|
if (!Files.exists(path)) {
|
||||||
|
log.error("License file does not exist: {}", filePath);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
log.info("Reading license from file: {}", filePath);
|
||||||
|
return Files.readString(path);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Failed to read license file: {}", e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's a direct license key
|
||||||
|
return keyOrFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
public void updateLicenseKey(String newKey) throws IOException {
|
public void updateLicenseKey(String newKey) throws IOException {
|
||||||
applicationProperties.getEnterpriseEdition().setKey(newKey);
|
applicationProperties.getPremium().setKey(newKey);
|
||||||
GeneralUtils.saveKeyToSettings("EnterpriseEdition.key", newKey);
|
GeneralUtils.saveKeyToSettings("EnterpriseEdition.key", newKey);
|
||||||
checkLicense();
|
checkLicense();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getEnterpriseEnabledResult() {
|
public boolean getEnterpriseEnabledResult() {
|
||||||
return enterpriseEnabledResult;
|
return premiumEnabledResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ public class AppConfig {
|
|||||||
@Bean(name = "analyticsEnabled")
|
@Bean(name = "analyticsEnabled")
|
||||||
@Scope("request")
|
@Scope("request")
|
||||||
public boolean analyticsEnabled() {
|
public boolean analyticsEnabled() {
|
||||||
if (applicationProperties.getEnterpriseEdition().isEnabled()) return true;
|
if (applicationProperties.getPremium().isEnabled()) return true;
|
||||||
return applicationProperties.getSystem().isAnalyticsEnabled();
|
return applicationProperties.getSystem().isAnalyticsEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@ -56,6 +57,8 @@ public class ConfigInitializer {
|
|||||||
YamlHelper settingsTemplateFile = new YamlHelper(tempTemplatePath);
|
YamlHelper settingsTemplateFile = new YamlHelper(tempTemplatePath);
|
||||||
YamlHelper settingsFile = new YamlHelper(settingTempPath);
|
YamlHelper settingsFile = new YamlHelper(settingTempPath);
|
||||||
|
|
||||||
|
migrateEnterpriseEditionToPremium(settingsFile, settingsTemplateFile);
|
||||||
|
|
||||||
boolean changesMade =
|
boolean changesMade =
|
||||||
settingsTemplateFile.updateValuesFromYaml(settingsFile, settingsTemplateFile);
|
settingsTemplateFile.updateValuesFromYaml(settingsFile, settingsTemplateFile);
|
||||||
if (changesMade) {
|
if (changesMade) {
|
||||||
@ -76,4 +79,46 @@ public class ConfigInitializer {
|
|||||||
log.info("Created custom_settings file: {}", customSettingsPath.toString());
|
log.info("Created custom_settings file: {}", customSettingsPath.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove post migration
|
||||||
|
private void migrateEnterpriseEditionToPremium(YamlHelper yaml, YamlHelper template) {
|
||||||
|
if (yaml.getValueByExactKeyPath("enterpriseEdition", "enabled") != null) {
|
||||||
|
template.updateValue(
|
||||||
|
List.of("premium", "enabled"),
|
||||||
|
yaml.getValueByExactKeyPath("enterpriseEdition", "enabled"));
|
||||||
|
}
|
||||||
|
if (yaml.getValueByExactKeyPath("enterpriseEdition", "key") != null) {
|
||||||
|
template.updateValue(
|
||||||
|
List.of("premium", "key"),
|
||||||
|
yaml.getValueByExactKeyPath("enterpriseEdition", "key"));
|
||||||
|
}
|
||||||
|
if (yaml.getValueByExactKeyPath("enterpriseEdition", "SSOAutoLogin") != null) {
|
||||||
|
template.updateValue(
|
||||||
|
List.of("premium", "proFeatures", "SSOAutoLogin"),
|
||||||
|
yaml.getValueByExactKeyPath("enterpriseEdition", "SSOAutoLogin"));
|
||||||
|
}
|
||||||
|
if (yaml.getValueByExactKeyPath("enterpriseEdition", "CustomMetadata", "autoUpdateMetadata")
|
||||||
|
!= null) {
|
||||||
|
template.updateValue(
|
||||||
|
List.of("premium", "proFeatures", "CustomMetadata", "autoUpdateMetadata"),
|
||||||
|
yaml.getValueByExactKeyPath(
|
||||||
|
"enterpriseEdition", "CustomMetadata", "autoUpdateMetadata"));
|
||||||
|
}
|
||||||
|
if (yaml.getValueByExactKeyPath("enterpriseEdition", "CustomMetadata", "author") != null) {
|
||||||
|
template.updateValue(
|
||||||
|
List.of("premium", "proFeatures", "CustomMetadata", "author"),
|
||||||
|
yaml.getValueByExactKeyPath("enterpriseEdition", "CustomMetadata", "author"));
|
||||||
|
}
|
||||||
|
if (yaml.getValueByExactKeyPath("enterpriseEdition", "CustomMetadata", "creator") != null) {
|
||||||
|
template.updateValue(
|
||||||
|
List.of("premium", "proFeatures", "CustomMetadata", "creator"),
|
||||||
|
yaml.getValueByExactKeyPath("enterpriseEdition", "CustomMetadata", "creator"));
|
||||||
|
}
|
||||||
|
if (yaml.getValueByExactKeyPath("enterpriseEdition", "CustomMetadata", "producer")
|
||||||
|
!= null) {
|
||||||
|
template.updateValue(
|
||||||
|
List.of("premium", "proFeatures", "CustomMetadata", "producer"),
|
||||||
|
yaml.getValueByExactKeyPath("enterpriseEdition", "CustomMetadata", "producer"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.stream.Collectors;
|
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.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -22,10 +23,14 @@ public class EndpointConfiguration {
|
|||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
|
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
|
||||||
private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>();
|
private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>();
|
||||||
|
private final boolean runningEE;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public EndpointConfiguration(ApplicationProperties applicationProperties) {
|
public EndpointConfiguration(
|
||||||
|
ApplicationProperties applicationProperties,
|
||||||
|
@Qualifier("runningEE") boolean runningEE) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
|
this.runningEE = runningEE;
|
||||||
init();
|
init();
|
||||||
processEnvironmentConfigs();
|
processEnvironmentConfigs();
|
||||||
}
|
}
|
||||||
@ -281,6 +286,13 @@ public class EndpointConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!runningEE) {
|
||||||
|
disableGroup("enterprise");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!applicationProperties.getSystem().getEnableUrlToPDF()) {
|
||||||
|
disableEndpoint("url-to-pdf");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getEndpointsForGroup(String group) {
|
public Set<String> getEndpointsForGroup(String group) {
|
||||||
|
@ -36,7 +36,6 @@ public class EndpointInspector implements ApplicationListener<ContextRefreshedEv
|
|||||||
if (!endpointsDiscovered) {
|
if (!endpointsDiscovered) {
|
||||||
discoverEndpoints();
|
discoverEndpoints();
|
||||||
endpointsDiscovered = true;
|
endpointsDiscovered = true;
|
||||||
logger.info("Discovered {} valid GET endpoints", validGetEndpoints.size());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
|
|
||||||
import stirling.software.SPDF.config.interfaces.SessionsInterface;
|
import stirling.software.SPDF.config.interfaces.SessionsInterface;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class EndpointInterceptor implements HandlerInterceptor {
|
public class EndpointInterceptor implements HandlerInterceptor {
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class EnterpriseEndpointFilter extends OncePerRequestFilter {
|
||||||
|
private final boolean runningEE;
|
||||||
|
|
||||||
|
public EnterpriseEndpointFilter(@Qualifier("runningEE") boolean runningEE) {
|
||||||
|
this.runningEE = runningEE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(
|
||||||
|
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
if (!runningEE && isPrometheusEndpointRequest(request)) {
|
||||||
|
response.setStatus(HttpStatus.NOT_FOUND.value());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPrometheusEndpointRequest(HttpServletRequest request) {
|
||||||
|
return request.getRequestURI().contains("/actuator/");
|
||||||
|
}
|
||||||
|
}
|
@ -125,8 +125,7 @@ public class MergeController {
|
|||||||
public ResponseEntity<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest form)
|
public ResponseEntity<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest form)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
List<File> filesToDelete = new ArrayList<>(); // List of temporary files to delete
|
List<File> filesToDelete = new ArrayList<>(); // List of temporary files to delete
|
||||||
ByteArrayOutputStream docOutputstream =
|
File mergedTempFile = null;
|
||||||
new ByteArrayOutputStream(); // Stream for the merged document
|
|
||||||
PDDocument mergedDocument = null;
|
PDDocument mergedDocument = null;
|
||||||
|
|
||||||
boolean removeCertSign = form.isRemoveCertSign();
|
boolean removeCertSign = form.isRemoveCertSign();
|
||||||
@ -139,21 +138,24 @@ public class MergeController {
|
|||||||
form.getSortType())); // Sort files based on the given sort type
|
form.getSortType())); // Sort files based on the given sort type
|
||||||
|
|
||||||
PDFMergerUtility mergerUtility = new PDFMergerUtility();
|
PDFMergerUtility mergerUtility = new PDFMergerUtility();
|
||||||
|
long totalSize = 0;
|
||||||
for (MultipartFile multipartFile : files) {
|
for (MultipartFile multipartFile : files) {
|
||||||
|
totalSize += multipartFile.getSize();
|
||||||
File tempFile =
|
File tempFile =
|
||||||
GeneralUtils.convertMultipartFileToFile(
|
GeneralUtils.convertMultipartFileToFile(
|
||||||
multipartFile); // Convert MultipartFile to File
|
multipartFile); // Convert MultipartFile to File
|
||||||
filesToDelete.add(tempFile); // Add temp file to the list for later deletion
|
filesToDelete.add(tempFile); // Add temp file to the list for later deletion
|
||||||
mergerUtility.addSource(tempFile); // Add source file to the merger utility
|
mergerUtility.addSource(tempFile); // Add source file to the merger utility
|
||||||
}
|
}
|
||||||
mergerUtility.setDestinationStream(
|
|
||||||
docOutputstream); // Set the output stream for the merged document
|
|
||||||
mergerUtility.mergeDocuments(null); // Merge the documents
|
|
||||||
|
|
||||||
byte[] mergedPdfBytes = docOutputstream.toByteArray(); // Get merged document bytes
|
mergedTempFile = Files.createTempFile("merged-", ".pdf").toFile();
|
||||||
|
mergerUtility.setDestinationFileName(mergedTempFile.getAbsolutePath());
|
||||||
|
|
||||||
|
mergerUtility.mergeDocuments(
|
||||||
|
pdfDocumentFactory.getStreamCacheFunction(totalSize)); // Merge the documents
|
||||||
|
|
||||||
// Load the merged PDF document
|
// Load the merged PDF document
|
||||||
mergedDocument = pdfDocumentFactory.load(mergedPdfBytes);
|
mergedDocument = pdfDocumentFactory.load(mergedTempFile);
|
||||||
|
|
||||||
// Remove signatures if removeCertSign is true
|
// Remove signatures if removeCertSign is true
|
||||||
if (removeCertSign) {
|
if (removeCertSign) {
|
||||||
@ -180,21 +182,23 @@ public class MergeController {
|
|||||||
String mergedFileName =
|
String mergedFileName =
|
||||||
files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||||
+ "_merged_unsigned.pdf";
|
+ "_merged_unsigned.pdf";
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.boasToWebResponse(
|
||||||
baos.toByteArray(), mergedFileName); // Return the modified PDF
|
baos, mergedFileName); // Return the modified PDF
|
||||||
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("Error in merge pdf process", ex);
|
log.error("Error in merge pdf process", ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
} finally {
|
} finally {
|
||||||
|
if (mergedDocument != null) {
|
||||||
|
mergedDocument.close(); // Close the merged document
|
||||||
|
}
|
||||||
for (File file : filesToDelete) {
|
for (File file : filesToDelete) {
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
Files.deleteIfExists(file.toPath()); // Delete temporary files
|
Files.deleteIfExists(file.toPath()); // Delete temporary files
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
docOutputstream.close();
|
if (mergedTempFile != null) {
|
||||||
if (mergedDocument != null) {
|
Files.deleteIfExists(mergedTempFile.toPath());
|
||||||
mergedDocument.close(); // Close the merged document
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,6 @@ public class SplitPdfBySizeController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
public SplitPdfBySizeController(CustomPDFDocumentFactory pdfDocumentFactory) {
|
public SplitPdfBySizeController(CustomPDFDocumentFactory pdfDocumentFactory) {
|
||||||
this.pdfDocumentFactory = pdfDocumentFactory;
|
this.pdfDocumentFactory = pdfDocumentFactory;
|
||||||
log.info(
|
|
||||||
"SplitPdfBySizeController initialized with pdfDocumentFactory: {}",
|
|
||||||
pdfDocumentFactory.getClass().getSimpleName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/split-by-size-or-count", consumes = "multipart/form-data")
|
@PostMapping(value = "/split-by-size-or-count", consumes = "multipart/form-data")
|
||||||
@ -57,53 +54,49 @@ public class SplitPdfBySizeController {
|
|||||||
public ResponseEntity<byte[]> autoSplitPdf(@ModelAttribute SplitPdfBySizeOrCountRequest request)
|
public ResponseEntity<byte[]> autoSplitPdf(@ModelAttribute SplitPdfBySizeOrCountRequest request)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
log.info("Starting PDF split process with request: {}", request);
|
log.debug("Starting PDF split process with request: {}", request);
|
||||||
MultipartFile file = request.getFileInput();
|
MultipartFile file = request.getFileInput();
|
||||||
log.info(
|
|
||||||
"File received: name={}, size={} bytes",
|
|
||||||
file.getOriginalFilename(),
|
|
||||||
file.getSize());
|
|
||||||
|
|
||||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||||
log.info("Created temporary zip file: {}", zipFile);
|
log.debug("Created temporary zip file: {}", zipFile);
|
||||||
|
|
||||||
String filename =
|
String filename =
|
||||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||||
.replaceFirst("[.][^.]+$", "");
|
.replaceFirst("[.][^.]+$", "");
|
||||||
log.info("Base filename for output: {}", filename);
|
log.debug("Base filename for output: {}", filename);
|
||||||
|
|
||||||
byte[] data = null;
|
byte[] data = null;
|
||||||
try {
|
try {
|
||||||
log.info("Reading input file bytes");
|
log.debug("Reading input file bytes");
|
||||||
byte[] pdfBytes = file.getBytes();
|
byte[] pdfBytes = file.getBytes();
|
||||||
log.info("Successfully read {} bytes from input file", pdfBytes.length);
|
log.debug("Successfully read {} bytes from input file", pdfBytes.length);
|
||||||
|
|
||||||
log.info("Creating ZIP output stream");
|
log.debug("Creating ZIP output stream");
|
||||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||||
log.info("Loading PDF document");
|
log.debug("Loading PDF document");
|
||||||
try (PDDocument sourceDocument = pdfDocumentFactory.load(pdfBytes)) {
|
try (PDDocument sourceDocument = pdfDocumentFactory.load(pdfBytes)) {
|
||||||
log.info(
|
log.debug(
|
||||||
"Successfully loaded PDF with {} pages",
|
"Successfully loaded PDF with {} pages",
|
||||||
sourceDocument.getNumberOfPages());
|
sourceDocument.getNumberOfPages());
|
||||||
|
|
||||||
int type = request.getSplitType();
|
int type = request.getSplitType();
|
||||||
String value = request.getSplitValue();
|
String value = request.getSplitValue();
|
||||||
log.info("Split type: {}, Split value: {}", type, value);
|
log.debug("Split type: {}, Split value: {}", type, value);
|
||||||
|
|
||||||
if (type == 0) {
|
if (type == 0) {
|
||||||
log.info("Processing split by size");
|
log.debug("Processing split by size");
|
||||||
long maxBytes = GeneralUtils.convertSizeToBytes(value);
|
long maxBytes = GeneralUtils.convertSizeToBytes(value);
|
||||||
log.info("Max bytes per document: {}", maxBytes);
|
log.debug("Max bytes per document: {}", maxBytes);
|
||||||
handleSplitBySize(sourceDocument, maxBytes, zipOut, filename);
|
handleSplitBySize(sourceDocument, maxBytes, zipOut, filename);
|
||||||
} else if (type == 1) {
|
} else if (type == 1) {
|
||||||
log.info("Processing split by page count");
|
log.debug("Processing split by page count");
|
||||||
int pageCount = Integer.parseInt(value);
|
int pageCount = Integer.parseInt(value);
|
||||||
log.info("Pages per document: {}", pageCount);
|
log.debug("Pages per document: {}", pageCount);
|
||||||
handleSplitByPageCount(sourceDocument, pageCount, zipOut, filename);
|
handleSplitByPageCount(sourceDocument, pageCount, zipOut, filename);
|
||||||
} else if (type == 2) {
|
} else if (type == 2) {
|
||||||
log.info("Processing split by document count");
|
log.debug("Processing split by document count");
|
||||||
int documentCount = Integer.parseInt(value);
|
int documentCount = Integer.parseInt(value);
|
||||||
log.info("Total number of documents: {}", documentCount);
|
log.debug("Total number of documents: {}", documentCount);
|
||||||
handleSplitByDocCount(sourceDocument, documentCount, zipOut, filename);
|
handleSplitByDocCount(sourceDocument, documentCount, zipOut, filename);
|
||||||
} else {
|
} else {
|
||||||
log.error("Invalid split type: {}", type);
|
log.error("Invalid split type: {}", type);
|
||||||
@ -111,7 +104,7 @@ public class SplitPdfBySizeController {
|
|||||||
"Invalid argument for split type: " + type);
|
"Invalid argument for split type: " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("PDF splitting completed successfully");
|
log.debug("PDF splitting completed successfully");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error loading or processing PDF document", e);
|
log.error("Error loading or processing PDF document", e);
|
||||||
throw e;
|
throw e;
|
||||||
@ -126,23 +119,23 @@ public class SplitPdfBySizeController {
|
|||||||
throw e; // Re-throw to ensure proper error response
|
throw e; // Re-throw to ensure proper error response
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
log.info("Reading ZIP file data");
|
log.debug("Reading ZIP file data");
|
||||||
data = Files.readAllBytes(zipFile);
|
data = Files.readAllBytes(zipFile);
|
||||||
log.info("Successfully read {} bytes from ZIP file", data.length);
|
log.debug("Successfully read {} bytes from ZIP file", data.length);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error reading ZIP file data", e);
|
log.error("Error reading ZIP file data", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info("Deleting temporary ZIP file");
|
log.debug("Deleting temporary ZIP file");
|
||||||
boolean deleted = Files.deleteIfExists(zipFile);
|
boolean deleted = Files.deleteIfExists(zipFile);
|
||||||
log.info("Temporary ZIP file deleted: {}", deleted);
|
log.debug("Temporary ZIP file deleted: {}", deleted);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error deleting temporary ZIP file", e);
|
log.error("Error deleting temporary ZIP file", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Returning response with {} bytes of data", data != null ? data.length : 0);
|
log.debug("Returning response with {} bytes of data", data != null ? data.length : 0);
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.bytesToWebResponse(
|
||||||
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
|
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||||
}
|
}
|
||||||
@ -150,7 +143,7 @@ public class SplitPdfBySizeController {
|
|||||||
private void handleSplitBySize(
|
private void handleSplitBySize(
|
||||||
PDDocument sourceDocument, long maxBytes, ZipOutputStream zipOut, String baseFilename)
|
PDDocument sourceDocument, long maxBytes, ZipOutputStream zipOut, String baseFilename)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
log.info("Starting handleSplitBySize with maxBytes={}", maxBytes);
|
log.debug("Starting handleSplitBySize with maxBytes={}", maxBytes);
|
||||||
|
|
||||||
PDDocument currentDoc =
|
PDDocument currentDoc =
|
||||||
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
||||||
@ -163,7 +156,7 @@ public class SplitPdfBySizeController {
|
|||||||
|
|
||||||
for (int pageIndex = 0; pageIndex < totalPages; pageIndex++) {
|
for (int pageIndex = 0; pageIndex < totalPages; pageIndex++) {
|
||||||
PDPage page = sourceDocument.getPage(pageIndex);
|
PDPage page = sourceDocument.getPage(pageIndex);
|
||||||
log.info("Processing page {} of {}", pageIndex + 1, totalPages);
|
log.debug("Processing page {} of {}", pageIndex + 1, totalPages);
|
||||||
|
|
||||||
// Add the page to current document
|
// Add the page to current document
|
||||||
PDPage newPage = new PDPage(page.getCOSObject());
|
PDPage newPage = new PDPage(page.getCOSObject());
|
||||||
@ -177,21 +170,21 @@ public class SplitPdfBySizeController {
|
|||||||
|| (pageAdded >= 20); // Always check after 20 pages
|
|| (pageAdded >= 20); // Always check after 20 pages
|
||||||
|
|
||||||
if (shouldCheckSize) {
|
if (shouldCheckSize) {
|
||||||
log.info("Performing size check after {} pages", pageAdded);
|
log.debug("Performing size check after {} pages", pageAdded);
|
||||||
ByteArrayOutputStream checkSizeStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream checkSizeStream = new ByteArrayOutputStream();
|
||||||
currentDoc.save(checkSizeStream);
|
currentDoc.save(checkSizeStream);
|
||||||
long actualSize = checkSizeStream.size();
|
long actualSize = checkSizeStream.size();
|
||||||
log.info("Current document size: {} bytes (max: {} bytes)", actualSize, maxBytes);
|
log.debug("Current document size: {} bytes (max: {} bytes)", actualSize, maxBytes);
|
||||||
|
|
||||||
if (actualSize > maxBytes) {
|
if (actualSize > maxBytes) {
|
||||||
// We exceeded the limit - remove the last page and save
|
// We exceeded the limit - remove the last page and save
|
||||||
if (currentDoc.getNumberOfPages() > 1) {
|
if (currentDoc.getNumberOfPages() > 1) {
|
||||||
currentDoc.removePage(currentDoc.getNumberOfPages() - 1);
|
currentDoc.removePage(currentDoc.getNumberOfPages() - 1);
|
||||||
pageIndex--; // Process this page again in the next document
|
pageIndex--; // Process this page again in the next document
|
||||||
log.info("Size limit exceeded - removed last page");
|
log.debug("Size limit exceeded - removed last page");
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(
|
log.debug(
|
||||||
"Saving document with {} pages as part {}",
|
"Saving document with {} pages as part {}",
|
||||||
currentDoc.getNumberOfPages(),
|
currentDoc.getNumberOfPages(),
|
||||||
fileIndex);
|
fileIndex);
|
||||||
@ -206,7 +199,7 @@ public class SplitPdfBySizeController {
|
|||||||
int pagesToLookAhead = Math.min(5, totalPages - pageIndex - 1);
|
int pagesToLookAhead = Math.min(5, totalPages - pageIndex - 1);
|
||||||
|
|
||||||
if (pagesToLookAhead > 0) {
|
if (pagesToLookAhead > 0) {
|
||||||
log.info(
|
log.debug(
|
||||||
"Testing {} upcoming pages for potential addition",
|
"Testing {} upcoming pages for potential addition",
|
||||||
pagesToLookAhead);
|
pagesToLookAhead);
|
||||||
|
|
||||||
@ -231,12 +224,12 @@ public class SplitPdfBySizeController {
|
|||||||
|
|
||||||
if (testSize <= maxBytes) {
|
if (testSize <= maxBytes) {
|
||||||
extraPagesAdded++;
|
extraPagesAdded++;
|
||||||
log.info(
|
log.debug(
|
||||||
"Test: Can add page {} (size would be {})",
|
"Test: Can add page {} (size would be {})",
|
||||||
testPageIndex + 1,
|
testPageIndex + 1,
|
||||||
testSize);
|
testSize);
|
||||||
} else {
|
} else {
|
||||||
log.info(
|
log.debug(
|
||||||
"Test: Cannot add page {} (size would be {})",
|
"Test: Cannot add page {} (size would be {})",
|
||||||
testPageIndex + 1,
|
testPageIndex + 1,
|
||||||
testSize);
|
testSize);
|
||||||
@ -248,7 +241,7 @@ public class SplitPdfBySizeController {
|
|||||||
|
|
||||||
// Add the pages we verified would fit
|
// Add the pages we verified would fit
|
||||||
if (extraPagesAdded > 0) {
|
if (extraPagesAdded > 0) {
|
||||||
log.info("Adding {} verified pages ahead", extraPagesAdded);
|
log.debug("Adding {} verified pages ahead", extraPagesAdded);
|
||||||
for (int i = 0; i < extraPagesAdded; i++) {
|
for (int i = 0; i < extraPagesAdded; i++) {
|
||||||
int extraPageIndex = pageIndex + 1 + i;
|
int extraPageIndex = pageIndex + 1 + i;
|
||||||
PDPage extraPage = sourceDocument.getPage(extraPageIndex);
|
PDPage extraPage = sourceDocument.getPage(extraPageIndex);
|
||||||
@ -265,26 +258,26 @@ public class SplitPdfBySizeController {
|
|||||||
|
|
||||||
// Save final document if it has any pages
|
// Save final document if it has any pages
|
||||||
if (currentDoc.getNumberOfPages() > 0) {
|
if (currentDoc.getNumberOfPages() > 0) {
|
||||||
log.info(
|
log.debug(
|
||||||
"Saving final document with {} pages as part {}",
|
"Saving final document with {} pages as part {}",
|
||||||
currentDoc.getNumberOfPages(),
|
currentDoc.getNumberOfPages(),
|
||||||
fileIndex);
|
fileIndex);
|
||||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Completed handleSplitBySize with {} document parts created", fileIndex - 1);
|
log.debug("Completed handleSplitBySize with {} document parts created", fileIndex - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSplitByPageCount(
|
private void handleSplitByPageCount(
|
||||||
PDDocument sourceDocument, int pageCount, ZipOutputStream zipOut, String baseFilename)
|
PDDocument sourceDocument, int pageCount, ZipOutputStream zipOut, String baseFilename)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
log.info("Starting handleSplitByPageCount with pageCount={}", pageCount);
|
log.debug("Starting handleSplitByPageCount with pageCount={}", pageCount);
|
||||||
int currentPageCount = 0;
|
int currentPageCount = 0;
|
||||||
log.info("Creating initial output document");
|
log.debug("Creating initial output document");
|
||||||
PDDocument currentDoc = null;
|
PDDocument currentDoc = null;
|
||||||
try {
|
try {
|
||||||
currentDoc = pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
currentDoc = pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
||||||
log.info("Successfully created initial output document");
|
log.debug("Successfully created initial output document");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error creating initial output document", e);
|
log.error("Error creating initial output document", e);
|
||||||
throw new IOException("Failed to create initial output document", e);
|
throw new IOException("Failed to create initial output document", e);
|
||||||
@ -293,49 +286,49 @@ public class SplitPdfBySizeController {
|
|||||||
int fileIndex = 1;
|
int fileIndex = 1;
|
||||||
int pageIndex = 0;
|
int pageIndex = 0;
|
||||||
int totalPages = sourceDocument.getNumberOfPages();
|
int totalPages = sourceDocument.getNumberOfPages();
|
||||||
log.info("Processing {} pages", totalPages);
|
log.debug("Processing {} pages", totalPages);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (PDPage page : sourceDocument.getPages()) {
|
for (PDPage page : sourceDocument.getPages()) {
|
||||||
pageIndex++;
|
pageIndex++;
|
||||||
log.info("Processing page {} of {}", pageIndex, totalPages);
|
log.debug("Processing page {} of {}", pageIndex, totalPages);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info("Adding page {} to current document", pageIndex);
|
log.debug("Adding page {} to current document", pageIndex);
|
||||||
currentDoc.addPage(page);
|
currentDoc.addPage(page);
|
||||||
log.info("Successfully added page {} to current document", pageIndex);
|
log.debug("Successfully added page {} to current document", pageIndex);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error adding page {} to current document", pageIndex, e);
|
log.error("Error adding page {} to current document", pageIndex, e);
|
||||||
throw new IOException("Failed to add page to document", e);
|
throw new IOException("Failed to add page to document", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPageCount++;
|
currentPageCount++;
|
||||||
log.info("Current page count: {}/{}", currentPageCount, pageCount);
|
log.debug("Current page count: {}/{}", currentPageCount, pageCount);
|
||||||
|
|
||||||
if (currentPageCount == pageCount) {
|
if (currentPageCount == pageCount) {
|
||||||
log.info(
|
log.debug(
|
||||||
"Reached target page count ({}), saving current document as part {}",
|
"Reached target page count ({}), saving current document as part {}",
|
||||||
pageCount,
|
pageCount,
|
||||||
fileIndex);
|
fileIndex);
|
||||||
try {
|
try {
|
||||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||||
log.info("Successfully saved document part {}", fileIndex - 1);
|
log.debug("Successfully saved document part {}", fileIndex - 1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error saving document part {}", fileIndex - 1, e);
|
log.error("Error saving document part {}", fileIndex - 1, e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info("Creating new document for next part");
|
log.debug("Creating new document for next part");
|
||||||
currentDoc = new PDDocument();
|
currentDoc = new PDDocument();
|
||||||
log.info("Successfully created new document");
|
log.debug("Successfully created new document");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error creating new document for next part", e);
|
log.error("Error creating new document for next part", e);
|
||||||
throw new IOException("Failed to create new document", e);
|
throw new IOException("Failed to create new document", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPageCount = 0;
|
currentPageCount = 0;
|
||||||
log.info("Reset current page count to 0");
|
log.debug("Reset current page count to 0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -346,34 +339,34 @@ public class SplitPdfBySizeController {
|
|||||||
// Add the last document if it contains any pages
|
// Add the last document if it contains any pages
|
||||||
try {
|
try {
|
||||||
if (currentDoc.getPages().getCount() != 0) {
|
if (currentDoc.getPages().getCount() != 0) {
|
||||||
log.info(
|
log.debug(
|
||||||
"Saving final document with {} pages as part {}",
|
"Saving final document with {} pages as part {}",
|
||||||
currentDoc.getPages().getCount(),
|
currentDoc.getPages().getCount(),
|
||||||
fileIndex);
|
fileIndex);
|
||||||
try {
|
try {
|
||||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||||
log.info("Successfully saved final document part {}", fileIndex - 1);
|
log.debug("Successfully saved final document part {}", fileIndex - 1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error saving final document part {}", fileIndex - 1, e);
|
log.error("Error saving final document part {}", fileIndex - 1, e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.info("Final document has no pages, skipping");
|
log.debug("Final document has no pages, skipping");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error checking or saving final document", e);
|
log.error("Error checking or saving final document", e);
|
||||||
throw new IOException("Failed to process final document", e);
|
throw new IOException("Failed to process final document", e);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
log.info("Closing final document");
|
log.debug("Closing final document");
|
||||||
currentDoc.close();
|
currentDoc.close();
|
||||||
log.info("Successfully closed final document");
|
log.debug("Successfully closed final document");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error closing final document", e);
|
log.error("Error closing final document", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Completed handleSplitByPageCount with {} document parts created", fileIndex - 1);
|
log.debug("Completed handleSplitByPageCount with {} document parts created", fileIndex - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSplitByDocCount(
|
private void handleSplitByDocCount(
|
||||||
@ -382,40 +375,40 @@ public class SplitPdfBySizeController {
|
|||||||
ZipOutputStream zipOut,
|
ZipOutputStream zipOut,
|
||||||
String baseFilename)
|
String baseFilename)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
log.info("Starting handleSplitByDocCount with documentCount={}", documentCount);
|
log.debug("Starting handleSplitByDocCount with documentCount={}", documentCount);
|
||||||
int totalPageCount = sourceDocument.getNumberOfPages();
|
int totalPageCount = sourceDocument.getNumberOfPages();
|
||||||
log.info("Total pages in source document: {}", totalPageCount);
|
log.debug("Total pages in source document: {}", totalPageCount);
|
||||||
|
|
||||||
int pagesPerDocument = totalPageCount / documentCount;
|
int pagesPerDocument = totalPageCount / documentCount;
|
||||||
int extraPages = totalPageCount % documentCount;
|
int extraPages = totalPageCount % documentCount;
|
||||||
log.info("Pages per document: {}, Extra pages: {}", pagesPerDocument, extraPages);
|
log.debug("Pages per document: {}, Extra pages: {}", pagesPerDocument, extraPages);
|
||||||
|
|
||||||
int currentPageIndex = 0;
|
int currentPageIndex = 0;
|
||||||
int fileIndex = 1;
|
int fileIndex = 1;
|
||||||
|
|
||||||
for (int i = 0; i < documentCount; i++) {
|
for (int i = 0; i < documentCount; i++) {
|
||||||
log.info("Creating document {} of {}", i + 1, documentCount);
|
log.debug("Creating document {} of {}", i + 1, documentCount);
|
||||||
PDDocument currentDoc = null;
|
PDDocument currentDoc = null;
|
||||||
try {
|
try {
|
||||||
currentDoc = pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
currentDoc = pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument);
|
||||||
log.info("Successfully created document {} of {}", i + 1, documentCount);
|
log.debug("Successfully created document {} of {}", i + 1, documentCount);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error creating document {} of {}", i + 1, documentCount, e);
|
log.error("Error creating document {} of {}", i + 1, documentCount, e);
|
||||||
throw new IOException("Failed to create document", e);
|
throw new IOException("Failed to create document", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0);
|
int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0);
|
||||||
log.info("Adding {} pages to document {}", pagesToAdd, i + 1);
|
log.debug("Adding {} pages to document {}", pagesToAdd, i + 1);
|
||||||
|
|
||||||
for (int j = 0; j < pagesToAdd; j++) {
|
for (int j = 0; j < pagesToAdd; j++) {
|
||||||
try {
|
try {
|
||||||
log.info(
|
log.debug(
|
||||||
"Adding page {} (index {}) to document {}",
|
"Adding page {} (index {}) to document {}",
|
||||||
j + 1,
|
j + 1,
|
||||||
currentPageIndex,
|
currentPageIndex,
|
||||||
i + 1);
|
i + 1);
|
||||||
currentDoc.addPage(sourceDocument.getPage(currentPageIndex));
|
currentDoc.addPage(sourceDocument.getPage(currentPageIndex));
|
||||||
log.info("Successfully added page {} to document {}", j + 1, i + 1);
|
log.debug("Successfully added page {} to document {}", j + 1, i + 1);
|
||||||
currentPageIndex++;
|
currentPageIndex++;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error adding page {} to document {}", j + 1, i + 1, e);
|
log.error("Error adding page {} to document {}", j + 1, i + 1, e);
|
||||||
@ -424,37 +417,37 @@ public class SplitPdfBySizeController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info("Saving document {} with {} pages", i + 1, pagesToAdd);
|
log.debug("Saving document {} with {} pages", i + 1, pagesToAdd);
|
||||||
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++);
|
||||||
log.info("Successfully saved document {}", i + 1);
|
log.debug("Successfully saved document {}", i + 1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error saving document {}", i + 1, e);
|
log.error("Error saving document {}", i + 1, e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Completed handleSplitByDocCount with {} documents created", documentCount);
|
log.debug("Completed handleSplitByDocCount with {} documents created", documentCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveDocumentToZip(
|
private void saveDocumentToZip(
|
||||||
PDDocument document, ZipOutputStream zipOut, String baseFilename, int index)
|
PDDocument document, ZipOutputStream zipOut, String baseFilename, int index)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
log.info("Starting saveDocumentToZip for document part {}", index);
|
log.debug("Starting saveDocumentToZip for document part {}", index);
|
||||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info("Saving document part {} to byte array", index);
|
log.debug("Saving document part {} to byte array", index);
|
||||||
document.save(outStream);
|
document.save(outStream);
|
||||||
log.info("Successfully saved document part {} ({} bytes)", index, outStream.size());
|
log.debug("Successfully saved document part {} ({} bytes)", index, outStream.size());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error saving document part {} to byte array", index, e);
|
log.error("Error saving document part {} to byte array", index, e);
|
||||||
throw new IOException("Failed to save document to byte array", e);
|
throw new IOException("Failed to save document to byte array", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info("Closing document part {}", index);
|
log.debug("Closing document part {}", index);
|
||||||
document.close();
|
document.close();
|
||||||
log.info("Successfully closed document part {}", index);
|
log.debug("Successfully closed document part {}", index);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error closing document part {}", index, e);
|
log.error("Error closing document part {}", index, e);
|
||||||
// Continue despite close error
|
// Continue despite close error
|
||||||
@ -463,17 +456,17 @@ public class SplitPdfBySizeController {
|
|||||||
try {
|
try {
|
||||||
// Create a new zip entry
|
// Create a new zip entry
|
||||||
String entryName = baseFilename + "_" + index + ".pdf";
|
String entryName = baseFilename + "_" + index + ".pdf";
|
||||||
log.info("Creating ZIP entry: {}", entryName);
|
log.debug("Creating ZIP entry: {}", entryName);
|
||||||
ZipEntry zipEntry = new ZipEntry(entryName);
|
ZipEntry zipEntry = new ZipEntry(entryName);
|
||||||
zipOut.putNextEntry(zipEntry);
|
zipOut.putNextEntry(zipEntry);
|
||||||
|
|
||||||
byte[] bytes = outStream.toByteArray();
|
byte[] bytes = outStream.toByteArray();
|
||||||
log.info("Writing {} bytes to ZIP entry", bytes.length);
|
log.debug("Writing {} bytes to ZIP entry", bytes.length);
|
||||||
zipOut.write(bytes);
|
zipOut.write(bytes);
|
||||||
|
|
||||||
log.info("Closing ZIP entry");
|
log.debug("Closing ZIP entry");
|
||||||
zipOut.closeEntry();
|
zipOut.closeEntry();
|
||||||
log.info("Successfully added document part {} to ZIP", index);
|
log.debug("Successfully added document part {} to ZIP", index);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error adding document part {} to ZIP", index, e);
|
log.error("Error adding document part {} to ZIP", index, e);
|
||||||
throw new IOException("Failed to add document to ZIP file", e);
|
throw new IOException("Failed to add document to ZIP file", e);
|
||||||
|
@ -30,6 +30,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import stirling.software.SPDF.config.security.UserService;
|
import stirling.software.SPDF.config.security.UserService;
|
||||||
import stirling.software.SPDF.config.security.UserUtils;
|
import stirling.software.SPDF.config.security.UserUtils;
|
||||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.AuthenticationType;
|
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;
|
||||||
@ -45,10 +46,15 @@ public class UserController {
|
|||||||
private static final String LOGIN_MESSAGETYPE_CREDSUPDATED = "/login?messageType=credsUpdated";
|
private static final String LOGIN_MESSAGETYPE_CREDSUPDATED = "/login?messageType=credsUpdated";
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final SessionPersistentRegistry sessionRegistry;
|
private final SessionPersistentRegistry sessionRegistry;
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
public UserController(UserService userService, SessionPersistentRegistry sessionRegistry) {
|
public UserController(
|
||||||
|
UserService userService,
|
||||||
|
SessionPersistentRegistry sessionRegistry,
|
||||||
|
ApplicationProperties applicationProperties) {
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
this.sessionRegistry = sessionRegistry;
|
this.sessionRegistry = sessionRegistry;
|
||||||
|
this.applicationProperties = applicationProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
@ -192,39 +198,44 @@ public class UserController {
|
|||||||
boolean forceChange)
|
boolean forceChange)
|
||||||
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
||||||
if (!userService.isUsernameValid(username)) {
|
if (!userService.isUsernameValid(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=invalidUsername", true);
|
return new RedirectView("/adminSettings?messageType=invalidUsername", true);
|
||||||
|
}
|
||||||
|
if (applicationProperties.getPremium().isEnabled()
|
||||||
|
&& applicationProperties.getPremium().getMaxUsers()
|
||||||
|
<= userService.getTotalUsersCount()) {
|
||||||
|
return new RedirectView("/adminSettings?messageType=maxUsersReached", true);
|
||||||
}
|
}
|
||||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
||||||
if (userOpt.isPresent()) {
|
if (userOpt.isPresent()) {
|
||||||
User user = userOpt.get();
|
User user = userOpt.get();
|
||||||
if (user.getUsername().equalsIgnoreCase(username)) {
|
if (user.getUsername().equalsIgnoreCase(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=usernameExists", true);
|
return new RedirectView("/adminSettings?messageType=usernameExists", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (userService.usernameExistsIgnoreCase(username)) {
|
if (userService.usernameExistsIgnoreCase(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=usernameExists", true);
|
return new RedirectView("/adminSettings?messageType=usernameExists", true);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// Validate the role
|
// Validate the role
|
||||||
Role roleEnum = Role.fromString(role);
|
Role roleEnum = Role.fromString(role);
|
||||||
if (roleEnum == Role.INTERNAL_API_USER) {
|
if (roleEnum == Role.INTERNAL_API_USER) {
|
||||||
// If the role is INTERNAL_API_USER, reject the request
|
// If the role is INTERNAL_API_USER, reject the request
|
||||||
return new RedirectView("/addUsers?messageType=invalidRole", true);
|
return new RedirectView("/adminSettings?messageType=invalidRole", true);
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// If the role ID is not valid, redirect with an error message
|
// If the role ID is not valid, redirect with an error message
|
||||||
return new RedirectView("/addUsers?messageType=invalidRole", true);
|
return new RedirectView("/adminSettings?messageType=invalidRole", true);
|
||||||
}
|
}
|
||||||
if (authType.equalsIgnoreCase(AuthenticationType.SSO.toString())) {
|
if (authType.equalsIgnoreCase(AuthenticationType.SSO.toString())) {
|
||||||
userService.saveUser(username, AuthenticationType.SSO, role);
|
userService.saveUser(username, AuthenticationType.SSO, role);
|
||||||
} else {
|
} else {
|
||||||
if (password.isBlank()) {
|
if (password.isBlank()) {
|
||||||
return new RedirectView("/addUsers?messageType=invalidPassword", true);
|
return new RedirectView("/adminSettings?messageType=invalidPassword", true);
|
||||||
}
|
}
|
||||||
userService.saveUser(username, password, role, forceChange);
|
userService.saveUser(username, password, role, forceChange);
|
||||||
}
|
}
|
||||||
return new RedirectView(
|
return new RedirectView(
|
||||||
"/addUsers", // Redirect to account page after adding the user
|
"/adminSettings", // Redirect to account page after adding the user
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,32 +248,32 @@ public class UserController {
|
|||||||
throws SQLException, UnsupportedProviderException {
|
throws SQLException, UnsupportedProviderException {
|
||||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
||||||
if (!userOpt.isPresent()) {
|
if (!userOpt.isPresent()) {
|
||||||
return new RedirectView("/addUsers?messageType=userNotFound", true);
|
return new RedirectView("/adminSettings?messageType=userNotFound", true);
|
||||||
}
|
}
|
||||||
if (!userService.usernameExistsIgnoreCase(username)) {
|
if (!userService.usernameExistsIgnoreCase(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=userNotFound", true);
|
return new RedirectView("/adminSettings?messageType=userNotFound", true);
|
||||||
}
|
}
|
||||||
// Get the currently authenticated username
|
// Get the currently authenticated username
|
||||||
String currentUsername = authentication.getName();
|
String currentUsername = authentication.getName();
|
||||||
// Check if the provided username matches the current session's username
|
// Check if the provided username matches the current session's username
|
||||||
if (currentUsername.equalsIgnoreCase(username)) {
|
if (currentUsername.equalsIgnoreCase(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=downgradeCurrentUser", true);
|
return new RedirectView("/adminSettings?messageType=downgradeCurrentUser", true);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// Validate the role
|
// Validate the role
|
||||||
Role roleEnum = Role.fromString(role);
|
Role roleEnum = Role.fromString(role);
|
||||||
if (roleEnum == Role.INTERNAL_API_USER) {
|
if (roleEnum == Role.INTERNAL_API_USER) {
|
||||||
// If the role is INTERNAL_API_USER, reject the request
|
// If the role is INTERNAL_API_USER, reject the request
|
||||||
return new RedirectView("/addUsers?messageType=invalidRole", true);
|
return new RedirectView("/adminSettings?messageType=invalidRole", true);
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// If the role ID is not valid, redirect with an error message
|
// If the role ID is not valid, redirect with an error message
|
||||||
return new RedirectView("/addUsers?messageType=invalidRole", true);
|
return new RedirectView("/adminSettings?messageType=invalidRole", true);
|
||||||
}
|
}
|
||||||
User user = userOpt.get();
|
User user = userOpt.get();
|
||||||
userService.changeRole(user, role);
|
userService.changeRole(user, role);
|
||||||
return new RedirectView(
|
return new RedirectView(
|
||||||
"/addUsers", // Redirect to account page after adding the user
|
"/adminSettings", // Redirect to account page after adding the user
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,16 +286,16 @@ public class UserController {
|
|||||||
throws SQLException, UnsupportedProviderException {
|
throws SQLException, UnsupportedProviderException {
|
||||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
||||||
if (userOpt.isEmpty()) {
|
if (userOpt.isEmpty()) {
|
||||||
return new RedirectView("/addUsers?messageType=userNotFound", true);
|
return new RedirectView("/adminSettings?messageType=userNotFound", true);
|
||||||
}
|
}
|
||||||
if (!userService.usernameExistsIgnoreCase(username)) {
|
if (!userService.usernameExistsIgnoreCase(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=userNotFound", true);
|
return new RedirectView("/adminSettings?messageType=userNotFound", true);
|
||||||
}
|
}
|
||||||
// Get the currently authenticated username
|
// Get the currently authenticated username
|
||||||
String currentUsername = authentication.getName();
|
String currentUsername = authentication.getName();
|
||||||
// Check if the provided username matches the current session's username
|
// Check if the provided username matches the current session's username
|
||||||
if (currentUsername.equalsIgnoreCase(username)) {
|
if (currentUsername.equalsIgnoreCase(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=disabledCurrentUser", true);
|
return new RedirectView("/adminSettings?messageType=disabledCurrentUser", true);
|
||||||
}
|
}
|
||||||
User user = userOpt.get();
|
User user = userOpt.get();
|
||||||
userService.changeUserEnabled(user, enabled);
|
userService.changeUserEnabled(user, enabled);
|
||||||
@ -304,7 +315,7 @@ public class UserController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new RedirectView(
|
return new RedirectView(
|
||||||
"/addUsers", // Redirect to account page after adding the user
|
"/adminSettings", // Redirect to account page after adding the user
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,13 +324,13 @@ public class UserController {
|
|||||||
public RedirectView deleteUser(
|
public RedirectView deleteUser(
|
||||||
@PathVariable("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("/adminSettings?messageType=deleteUsernameExists", true);
|
||||||
}
|
}
|
||||||
// Get the currently authenticated username
|
// Get the currently authenticated username
|
||||||
String currentUsername = authentication.getName();
|
String currentUsername = authentication.getName();
|
||||||
// Check if the provided username matches the current session's username
|
// Check if the provided username matches the current session's username
|
||||||
if (currentUsername.equalsIgnoreCase(username)) {
|
if (currentUsername.equalsIgnoreCase(username)) {
|
||||||
return new RedirectView("/addUsers?messageType=deleteCurrentUser", true);
|
return new RedirectView("/adminSettings?messageType=deleteCurrentUser", true);
|
||||||
}
|
}
|
||||||
// Invalidate all sessions before deleting the user
|
// Invalidate all sessions before deleting the user
|
||||||
List<SessionInformation> sessionsInformations =
|
List<SessionInformation> sessionsInformations =
|
||||||
@ -329,7 +340,7 @@ public class UserController {
|
|||||||
sessionRegistry.removeSessionInformation(sessionsInformation.getSessionId());
|
sessionRegistry.removeSessionInformation(sessionsInformation.getSessionId());
|
||||||
}
|
}
|
||||||
userService.deleteUser(username);
|
userService.deleteUser(username);
|
||||||
return new RedirectView("/addUsers", true);
|
return new RedirectView("/adminSettings", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
|
@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.SPDF.config.RuntimePathConfig;
|
import stirling.software.SPDF.config.RuntimePathConfig;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.api.converters.UrlToPdfRequest;
|
import stirling.software.SPDF.model.api.converters.UrlToPdfRequest;
|
||||||
import stirling.software.SPDF.service.CustomPDFDocumentFactory;
|
import stirling.software.SPDF.service.CustomPDFDocumentFactory;
|
||||||
import stirling.software.SPDF.utils.GeneralUtils;
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
@ -35,12 +36,16 @@ public class ConvertWebsiteToPDF {
|
|||||||
|
|
||||||
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
||||||
private final RuntimePathConfig runtimePathConfig;
|
private final RuntimePathConfig runtimePathConfig;
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ConvertWebsiteToPDF(
|
public ConvertWebsiteToPDF(
|
||||||
CustomPDFDocumentFactory pdfDocumentFactory, RuntimePathConfig runtimePathConfig) {
|
CustomPDFDocumentFactory pdfDocumentFactory,
|
||||||
|
RuntimePathConfig runtimePathConfig,
|
||||||
|
ApplicationProperties applicationProperties) {
|
||||||
this.pdfDocumentFactory = pdfDocumentFactory;
|
this.pdfDocumentFactory = pdfDocumentFactory;
|
||||||
this.runtimePathConfig = runtimePathConfig;
|
this.runtimePathConfig = runtimePathConfig;
|
||||||
|
this.applicationProperties = applicationProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
|
@PostMapping(consumes = "multipart/form-data", value = "/url/pdf")
|
||||||
@ -53,6 +58,9 @@ public class ConvertWebsiteToPDF {
|
|||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
String URL = request.getUrlInput();
|
String URL = request.getUrlInput();
|
||||||
|
|
||||||
|
if (!applicationProperties.getSystem().getEnableUrlToPDF()) {
|
||||||
|
throw new IllegalArgumentException("This endpoint has been disabled by the admin.");
|
||||||
|
}
|
||||||
// Validate the URL format
|
// Validate the URL format
|
||||||
if (!URL.matches("^https?://.*") || !GeneralUtils.isValidURL(URL)) {
|
if (!URL.matches("^https?://.*") || !GeneralUtils.isValidURL(URL)) {
|
||||||
throw new IllegalArgumentException("Invalid URL format provided.");
|
throw new IllegalArgumentException("Invalid URL format provided.");
|
||||||
|
@ -86,7 +86,7 @@ public class MetadataController {
|
|||||||
allRequestParams = new java.util.HashMap<String, String>();
|
allRequestParams = new java.util.HashMap<String, String>();
|
||||||
}
|
}
|
||||||
// Load the PDF file into a PDDocument
|
// Load the PDF file into a PDDocument
|
||||||
PDDocument document = pdfDocumentFactory.load(pdfFile);
|
PDDocument document = pdfDocumentFactory.load(pdfFile, true);
|
||||||
|
|
||||||
// Get the document information from the PDF
|
// Get the document information from the PDF
|
||||||
PDDocumentInformation info = document.getDocumentInformation();
|
PDDocumentInformation info = document.getDocumentInformation();
|
||||||
|
@ -51,11 +51,12 @@ public class SanitizeController {
|
|||||||
MultipartFile inputFile = request.getFileInput();
|
MultipartFile inputFile = request.getFileInput();
|
||||||
boolean removeJavaScript = request.isRemoveJavaScript();
|
boolean removeJavaScript = request.isRemoveJavaScript();
|
||||||
boolean removeEmbeddedFiles = request.isRemoveEmbeddedFiles();
|
boolean removeEmbeddedFiles = request.isRemoveEmbeddedFiles();
|
||||||
|
boolean removeXMPMetadata = request.isRemoveXMPMetadata();
|
||||||
boolean removeMetadata = request.isRemoveMetadata();
|
boolean removeMetadata = request.isRemoveMetadata();
|
||||||
boolean removeLinks = request.isRemoveLinks();
|
boolean removeLinks = request.isRemoveLinks();
|
||||||
boolean removeFonts = request.isRemoveFonts();
|
boolean removeFonts = request.isRemoveFonts();
|
||||||
|
|
||||||
PDDocument document = pdfDocumentFactory.load(inputFile);
|
PDDocument document = pdfDocumentFactory.load(inputFile, true);
|
||||||
if (removeJavaScript) {
|
if (removeJavaScript) {
|
||||||
sanitizeJavaScript(document);
|
sanitizeJavaScript(document);
|
||||||
}
|
}
|
||||||
@ -64,8 +65,12 @@ public class SanitizeController {
|
|||||||
sanitizeEmbeddedFiles(document);
|
sanitizeEmbeddedFiles(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (removeXMPMetadata) {
|
||||||
|
sanitizeXMPMetadata(document);
|
||||||
|
}
|
||||||
|
|
||||||
if (removeMetadata) {
|
if (removeMetadata) {
|
||||||
sanitizeMetadata(document);
|
sanitizeDocumentInfoMetadata(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeLinks) {
|
if (removeLinks) {
|
||||||
@ -145,7 +150,7 @@ public class SanitizeController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sanitizeMetadata(PDDocument document) {
|
private void sanitizeXMPMetadata(PDDocument document) {
|
||||||
if (document.getDocumentCatalog() != null) {
|
if (document.getDocumentCatalog() != null) {
|
||||||
PDMetadata metadata = document.getDocumentCatalog().getMetadata();
|
PDMetadata metadata = document.getDocumentCatalog().getMetadata();
|
||||||
if (metadata != null) {
|
if (metadata != null) {
|
||||||
@ -154,6 +159,16 @@ public class SanitizeController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sanitizeDocumentInfoMetadata(PDDocument document) {
|
||||||
|
PDDocumentInformation docInfo = document.getDocumentInformation();
|
||||||
|
if (docInfo != null) {
|
||||||
|
PDDocumentInformation newInfo = new PDDocumentInformation();
|
||||||
|
document.setDocumentInformation(newInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void sanitizeLinks(PDDocument document) throws IOException {
|
private void sanitizeLinks(PDDocument document) throws IOException {
|
||||||
for (PDPage page : document.getPages()) {
|
for (PDPage page : document.getPages()) {
|
||||||
for (PDAnnotation annotation : page.getAnnotations()) {
|
for (PDAnnotation annotation : page.getAnnotations()) {
|
||||||
|
@ -123,11 +123,11 @@ public class AccountWebController {
|
|||||||
|
|
||||||
if (securityProps.isSaml2Active()
|
if (securityProps.isSaml2Active()
|
||||||
&& applicationProperties.getSystem().getEnableAlphaFunctionality()
|
&& applicationProperties.getSystem().getEnableAlphaFunctionality()
|
||||||
&& applicationProperties.getEnterpriseEdition().isEnabled()) {
|
&& applicationProperties.getPremium().isEnabled()) {
|
||||||
String samlIdp = saml2.getProvider();
|
String samlIdp = saml2.getProvider();
|
||||||
String saml2AuthenticationPath = "/saml2/authenticate/" + saml2.getRegistrationId();
|
String saml2AuthenticationPath = "/saml2/authenticate/" + saml2.getRegistrationId();
|
||||||
|
|
||||||
if (applicationProperties.getEnterpriseEdition().isSsoAutoLogin()) {
|
if (applicationProperties.getPremium().getProFeatures().isSsoAutoLogin()) {
|
||||||
return "redirect:" + request.getRequestURL() + saml2AuthenticationPath;
|
return "redirect:" + request.getRequestURL() + saml2AuthenticationPath;
|
||||||
} else {
|
} else {
|
||||||
providerList.put(saml2AuthenticationPath, samlIdp + " (SAML 2)");
|
providerList.put(saml2AuthenticationPath, samlIdp + " (SAML 2)");
|
||||||
@ -201,7 +201,13 @@ public class AccountWebController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@GetMapping("/addUsers")
|
@GetMapping("/usage")
|
||||||
|
public String showUsage() {
|
||||||
|
return "usage";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
|
@GetMapping("/adminSettings")
|
||||||
public String showAddUserForm(
|
public String showAddUserForm(
|
||||||
HttpServletRequest request, Model model, Authentication authentication) {
|
HttpServletRequest request, Model model, Authentication authentication) {
|
||||||
List<User> allUsers = userRepository.findAll();
|
List<User> allUsers = userRepository.findAll();
|
||||||
@ -337,7 +343,8 @@ public class AccountWebController {
|
|||||||
model.addAttribute("maxSessions", maxSessions);
|
model.addAttribute("maxSessions", maxSessions);
|
||||||
model.addAttribute("maxUserSessions", maxUserSessions);
|
model.addAttribute("maxUserSessions", maxUserSessions);
|
||||||
model.addAttribute("sessionCount", sessionCount);
|
model.addAttribute("sessionCount", sessionCount);
|
||||||
return "addUsers";
|
model.addAttribute("maxEnterpriseUsers", applicationProperties.getPremium().getMaxUsers());
|
||||||
|
return "adminSettings";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
|
@ -22,6 +22,7 @@ import jakarta.annotation.PostConstruct;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.config.EndpointInspector;
|
||||||
import stirling.software.SPDF.config.StartupApplicationListener;
|
import stirling.software.SPDF.config.StartupApplicationListener;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@ -32,15 +33,17 @@ import stirling.software.SPDF.model.ApplicationProperties;
|
|||||||
public class MetricsController {
|
public class MetricsController {
|
||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
private final MeterRegistry meterRegistry;
|
private final MeterRegistry meterRegistry;
|
||||||
|
private final EndpointInspector endpointInspector;
|
||||||
private boolean metricsEnabled;
|
private boolean metricsEnabled;
|
||||||
|
|
||||||
public MetricsController(
|
public MetricsController(
|
||||||
ApplicationProperties applicationProperties, MeterRegistry meterRegistry) {
|
ApplicationProperties applicationProperties,
|
||||||
|
MeterRegistry meterRegistry,
|
||||||
|
EndpointInspector endpointInspector) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.meterRegistry = meterRegistry;
|
this.meterRegistry = meterRegistry;
|
||||||
|
this.endpointInspector = endpointInspector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@ -208,25 +211,43 @@ public class MetricsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private double getRequestCount(String method, Optional<String> endpoint) {
|
private double getRequestCount(String method, Optional<String> endpoint) {
|
||||||
log.info(
|
return meterRegistry.find("http.requests").tag("method", method).counters().stream()
|
||||||
"Getting request count for method: {}, endpoint: {}",
|
.filter(
|
||||||
method,
|
counter -> {
|
||||||
endpoint.orElse("all"));
|
String uri = counter.getId().getTag("uri");
|
||||||
double count =
|
|
||||||
meterRegistry.find("http.requests").tag("method", method).counters().stream()
|
// Apply filtering logic - Skip if uri is null
|
||||||
.filter(
|
if (uri == null) {
|
||||||
counter ->
|
return false;
|
||||||
!endpoint.isPresent()
|
}
|
||||||
|| endpoint.get()
|
|
||||||
.equals(counter.getId().getTag("uri")))
|
// For POST requests, only include if they start with /api/v1
|
||||||
.mapToDouble(Counter::count)
|
if ("POST".equals(method) && !uri.contains("api/v1")) {
|
||||||
.sum();
|
return false;
|
||||||
log.info("Request count: {}", count);
|
}
|
||||||
return count;
|
|
||||||
|
if (uri.contains(".txt")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For GET requests, validate if we have a list of valid endpoints
|
||||||
|
final boolean validateGetEndpoints =
|
||||||
|
endpointInspector.getValidGetEndpoints().size() != 0;
|
||||||
|
if ("GET".equals(method)
|
||||||
|
&& validateGetEndpoints
|
||||||
|
&& !endpointInspector.isValidGetEndpoint(uri)) {
|
||||||
|
log.debug("Skipping invalid GET endpoint: {}", uri);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter for specific endpoint if provided
|
||||||
|
return !endpoint.isPresent() || endpoint.get().equals(uri);
|
||||||
|
})
|
||||||
|
.mapToDouble(Counter::count)
|
||||||
|
.sum();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<EndpointCount> getEndpointCounts(String method) {
|
private List<EndpointCount> getEndpointCounts(String method) {
|
||||||
log.info("Getting endpoint counts for method: {}", method);
|
|
||||||
Map<String, Double> counts = new HashMap<>();
|
Map<String, Double> counts = new HashMap<>();
|
||||||
meterRegistry
|
meterRegistry
|
||||||
.find("http.requests")
|
.find("http.requests")
|
||||||
@ -235,28 +256,72 @@ public class MetricsController {
|
|||||||
.forEach(
|
.forEach(
|
||||||
counter -> {
|
counter -> {
|
||||||
String uri = counter.getId().getTag("uri");
|
String uri = counter.getId().getTag("uri");
|
||||||
|
|
||||||
|
// Skip if uri is null
|
||||||
|
if (uri == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For POST requests, only include if they start with /api/v1
|
||||||
|
if ("POST".equals(method) && !uri.contains("api/v1")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri.contains(".txt")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For GET requests, validate if we have a list of valid endpoints
|
||||||
|
final boolean validateGetEndpoints =
|
||||||
|
endpointInspector.getValidGetEndpoints().size() != 0;
|
||||||
|
if ("GET".equals(method)
|
||||||
|
&& validateGetEndpoints
|
||||||
|
&& !endpointInspector.isValidGetEndpoint(uri)) {
|
||||||
|
log.debug("Skipping invalid GET endpoint: {}", uri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
counts.merge(uri, counter.count(), Double::sum);
|
counts.merge(uri, counter.count(), Double::sum);
|
||||||
});
|
});
|
||||||
List<EndpointCount> result =
|
|
||||||
counts.entrySet().stream()
|
return counts.entrySet().stream()
|
||||||
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue()))
|
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue()))
|
||||||
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
log.info("Found {} endpoints with counts", result.size());
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getUniqueUserCount(String method, Optional<String> endpoint) {
|
private double getUniqueUserCount(String method, Optional<String> endpoint) {
|
||||||
log.info(
|
|
||||||
"Getting unique user count for method: {}, endpoint: {}",
|
|
||||||
method,
|
|
||||||
endpoint.orElse("all"));
|
|
||||||
Set<String> uniqueUsers = new HashSet<>();
|
Set<String> uniqueUsers = new HashSet<>();
|
||||||
meterRegistry.find("http.requests").tag("method", method).counters().stream()
|
meterRegistry.find("http.requests").tag("method", method).counters().stream()
|
||||||
.filter(
|
.filter(
|
||||||
counter ->
|
counter -> {
|
||||||
!endpoint.isPresent()
|
String uri = counter.getId().getTag("uri");
|
||||||
|| endpoint.get().equals(counter.getId().getTag("uri")))
|
|
||||||
|
// Skip if uri is null
|
||||||
|
if (uri == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For POST requests, only include if they start with /api/v1
|
||||||
|
if ("POST".equals(method) && !uri.contains("api/v1")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri.contains(".txt")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For GET requests, validate if we have a list of valid endpoints
|
||||||
|
final boolean validateGetEndpoints =
|
||||||
|
endpointInspector.getValidGetEndpoints().size() != 0;
|
||||||
|
if ("GET".equals(method)
|
||||||
|
&& validateGetEndpoints
|
||||||
|
&& !endpointInspector.isValidGetEndpoint(uri)) {
|
||||||
|
log.debug("Skipping invalid GET endpoint: {}", uri);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !endpoint.isPresent() || endpoint.get().equals(uri);
|
||||||
|
})
|
||||||
.forEach(
|
.forEach(
|
||||||
counter -> {
|
counter -> {
|
||||||
String session = counter.getId().getTag("session");
|
String session = counter.getId().getTag("session");
|
||||||
@ -264,12 +329,10 @@ public class MetricsController {
|
|||||||
uniqueUsers.add(session);
|
uniqueUsers.add(session);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
log.info("Unique user count: {}", uniqueUsers.size());
|
|
||||||
return uniqueUsers.size();
|
return uniqueUsers.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<EndpointCount> getUniqueUserCounts(String method) {
|
private List<EndpointCount> getUniqueUserCounts(String method) {
|
||||||
log.info("Getting unique user counts for method: {}", method);
|
|
||||||
Map<String, Set<String>> uniqueUsers = new HashMap<>();
|
Map<String, Set<String>> uniqueUsers = new HashMap<>();
|
||||||
meterRegistry
|
meterRegistry
|
||||||
.find("http.requests")
|
.find("http.requests")
|
||||||
@ -283,13 +346,10 @@ public class MetricsController {
|
|||||||
uniqueUsers.computeIfAbsent(uri, k -> new HashSet<>()).add(session);
|
uniqueUsers.computeIfAbsent(uri, k -> new HashSet<>()).add(session);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
List<EndpointCount> result =
|
return uniqueUsers.entrySet().stream()
|
||||||
uniqueUsers.entrySet().stream()
|
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue().size()))
|
||||||
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue().size()))
|
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
||||||
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
.collect(Collectors.toList());
|
||||||
.collect(Collectors.toList());
|
|
||||||
log.info("Found {} endpoints with unique user counts", result.size());
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/uptime")
|
@GetMapping("/uptime")
|
||||||
|
@ -81,6 +81,8 @@ public class ApplicationProperties {
|
|||||||
private Endpoints endpoints = new Endpoints();
|
private Endpoints endpoints = new Endpoints();
|
||||||
private Metrics metrics = new Metrics();
|
private Metrics metrics = new Metrics();
|
||||||
private AutomaticallyGenerated automaticallyGenerated = new AutomaticallyGenerated();
|
private AutomaticallyGenerated automaticallyGenerated = new AutomaticallyGenerated();
|
||||||
|
|
||||||
|
private Premium premium = new Premium();
|
||||||
private EnterpriseEdition enterpriseEdition = new EnterpriseEdition();
|
private EnterpriseEdition enterpriseEdition = new EnterpriseEdition();
|
||||||
private AutoPipeline autoPipeline = new AutoPipeline();
|
private AutoPipeline autoPipeline = new AutoPipeline();
|
||||||
private ProcessExecutor processExecutor = new ProcessExecutor();
|
private ProcessExecutor processExecutor = new ProcessExecutor();
|
||||||
@ -287,6 +289,7 @@ public class ApplicationProperties {
|
|||||||
private Boolean enableAnalytics;
|
private Boolean enableAnalytics;
|
||||||
private Datasource datasource;
|
private Datasource datasource;
|
||||||
private Boolean disableSanitize;
|
private Boolean disableSanitize;
|
||||||
|
private Boolean enableUrlToPDF;
|
||||||
private CustomPaths customPaths = new CustomPaths();
|
private CustomPaths customPaths = new CustomPaths();
|
||||||
|
|
||||||
public boolean isAnalyticsEnabled() {
|
public boolean isAnalyticsEnabled() {
|
||||||
@ -390,6 +393,7 @@ public class ApplicationProperties {
|
|||||||
private String appVersion;
|
private String appVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove post migration
|
||||||
@Data
|
@Data
|
||||||
public static class EnterpriseEdition {
|
public static class EnterpriseEdition {
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
@ -415,6 +419,50 @@ public class ApplicationProperties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Premium {
|
||||||
|
private boolean enabled;
|
||||||
|
@ToString.Exclude private String key;
|
||||||
|
private int maxUsers;
|
||||||
|
private ProFeatures proFeatures = new ProFeatures();
|
||||||
|
private EnterpriseFeatures enterpriseFeatures = new EnterpriseFeatures();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class ProFeatures {
|
||||||
|
private boolean ssoAutoLogin;
|
||||||
|
private CustomMetadata customMetadata = new CustomMetadata();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class CustomMetadata {
|
||||||
|
private boolean autoUpdateMetadata;
|
||||||
|
private String author;
|
||||||
|
private String creator;
|
||||||
|
private String producer;
|
||||||
|
|
||||||
|
public String getCreator() {
|
||||||
|
return creator == null || creator.trim().isEmpty() ? "Stirling-PDF" : creator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProducer() {
|
||||||
|
return producer == null || producer.trim().isEmpty()
|
||||||
|
? "Stirling-PDF"
|
||||||
|
: producer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class EnterpriseFeatures {
|
||||||
|
private PersistentMetrics persistentMetrics = new PersistentMetrics();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class PersistentMetrics {
|
||||||
|
private boolean enabled;
|
||||||
|
private int retentionDays;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class ProcessExecutor {
|
public static class ProcessExecutor {
|
||||||
private SessionLimit sessionLimit = new SessionLimit();
|
private SessionLimit sessionLimit = new SessionLimit();
|
||||||
|
@ -17,7 +17,10 @@ public class SanitizePdfRequest extends PDFFile {
|
|||||||
@Schema(description = "Remove embedded files from the PDF", defaultValue = "false")
|
@Schema(description = "Remove embedded files from the PDF", defaultValue = "false")
|
||||||
private boolean removeEmbeddedFiles;
|
private boolean removeEmbeddedFiles;
|
||||||
|
|
||||||
@Schema(description = "Remove metadata from the PDF", defaultValue = "false")
|
@Schema(description = "Remove XMP metadata from the PDF", defaultValue = "false")
|
||||||
|
private boolean removeXMPMetadata;
|
||||||
|
|
||||||
|
@Schema(description = "Remove document info metadata from the PDF", defaultValue = "false")
|
||||||
private boolean removeMetadata;
|
private boolean removeMetadata;
|
||||||
|
|
||||||
@Schema(description = "Remove links from the PDF", defaultValue = "false")
|
@Schema(description = "Remove links from the PDF", defaultValue = "false")
|
||||||
|
@ -68,10 +68,18 @@ public class CustomPDFDocumentFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry point for loading a PDF document from a file. Automatically selects the most
|
* Main entry point for loading a PDF document from a file. Automatically selects the most
|
||||||
* appropriate loading strategy.
|
* appropriate loading strategy.
|
||||||
*/
|
*/
|
||||||
public PDDocument load(File file) throws IOException {
|
public PDDocument load(File file) throws IOException {
|
||||||
|
return load(file, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main entry point for loading a PDF document from a file with read-only option.
|
||||||
|
* Automatically selects the most appropriate loading strategy.
|
||||||
|
*/
|
||||||
|
public PDDocument load(File file, boolean readOnly) throws IOException {
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
throw new IllegalArgumentException("File cannot be null");
|
throw new IllegalArgumentException("File cannot be null");
|
||||||
}
|
}
|
||||||
@ -79,14 +87,26 @@ public class CustomPDFDocumentFactory {
|
|||||||
long fileSize = file.length();
|
long fileSize = file.length();
|
||||||
log.debug("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024));
|
log.debug("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024));
|
||||||
|
|
||||||
return loadAdaptively(file, fileSize);
|
PDDocument doc = loadAdaptively(file, fileSize);
|
||||||
|
if (!readOnly) {
|
||||||
|
postProcessDocument(doc);
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry point for loading a PDF document from a Path. Automatically selects the most
|
* Main entry point for loading a PDF document from a Path. Automatically selects the most
|
||||||
* appropriate loading strategy.
|
* appropriate loading strategy.
|
||||||
*/
|
*/
|
||||||
public PDDocument load(Path path) throws IOException {
|
public PDDocument load(Path path) throws IOException {
|
||||||
|
return load(path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main entry point for loading a PDF document from a Path with read-only option.
|
||||||
|
* Automatically selects the most appropriate loading strategy.
|
||||||
|
*/
|
||||||
|
public PDDocument load(Path path, boolean readOnly) throws IOException {
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
throw new IllegalArgumentException("File cannot be null");
|
throw new IllegalArgumentException("File cannot be null");
|
||||||
}
|
}
|
||||||
@ -94,11 +114,20 @@ public class CustomPDFDocumentFactory {
|
|||||||
long fileSize = Files.size(path);
|
long fileSize = Files.size(path);
|
||||||
log.debug("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024));
|
log.debug("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024));
|
||||||
|
|
||||||
return loadAdaptively(path.toFile(), fileSize);
|
PDDocument doc = loadAdaptively(path.toFile(), fileSize);
|
||||||
|
if (!readOnly) {
|
||||||
|
postProcessDocument(doc);
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load a PDF from byte array with automatic optimization. */
|
/** Load a PDF from byte array with automatic optimization. */
|
||||||
public PDDocument load(byte[] input) throws IOException {
|
public PDDocument load(byte[] input) throws IOException {
|
||||||
|
return load(input, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load a PDF from byte array with automatic optimization and read-only option. */
|
||||||
|
public PDDocument load(byte[] input, boolean readOnly) throws IOException {
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
throw new IllegalArgumentException("Input bytes cannot be null");
|
throw new IllegalArgumentException("Input bytes cannot be null");
|
||||||
}
|
}
|
||||||
@ -106,11 +135,20 @@ public class CustomPDFDocumentFactory {
|
|||||||
long dataSize = input.length;
|
long dataSize = input.length;
|
||||||
log.debug("Loading PDF from byte array, size: {}MB", dataSize / (1024 * 1024));
|
log.debug("Loading PDF from byte array, size: {}MB", dataSize / (1024 * 1024));
|
||||||
|
|
||||||
return loadAdaptively(input, dataSize);
|
PDDocument doc = loadAdaptively(input, dataSize);
|
||||||
|
if (!readOnly) {
|
||||||
|
postProcessDocument(doc);
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load a PDF from InputStream with automatic optimization. */
|
/** Load a PDF from InputStream with automatic optimization. */
|
||||||
public PDDocument load(InputStream input) throws IOException {
|
public PDDocument load(InputStream input) throws IOException {
|
||||||
|
return load(input, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load a PDF from InputStream with automatic optimization and read-only option. */
|
||||||
|
public PDDocument load(InputStream input, boolean readOnly) throws IOException {
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
throw new IllegalArgumentException("InputStream cannot be null");
|
throw new IllegalArgumentException("InputStream cannot be null");
|
||||||
}
|
}
|
||||||
@ -119,11 +157,20 @@ public class CustomPDFDocumentFactory {
|
|||||||
Path tempFile = createTempFile("pdf-stream-");
|
Path tempFile = createTempFile("pdf-stream-");
|
||||||
|
|
||||||
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
return loadAdaptively(tempFile.toFile(), Files.size(tempFile));
|
PDDocument doc = loadAdaptively(tempFile.toFile(), Files.size(tempFile));
|
||||||
|
if (!readOnly) {
|
||||||
|
postProcessDocument(doc);
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load with password from InputStream */
|
/** Load with password from InputStream */
|
||||||
public PDDocument load(InputStream input, String password) throws IOException {
|
public PDDocument load(InputStream input, String password) throws IOException {
|
||||||
|
return load(input, password, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load with password from InputStream and read-only option */
|
||||||
|
public PDDocument load(InputStream input, String password, boolean readOnly) throws IOException {
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
throw new IllegalArgumentException("InputStream cannot be null");
|
throw new IllegalArgumentException("InputStream cannot be null");
|
||||||
}
|
}
|
||||||
@ -132,14 +179,59 @@ public class CustomPDFDocumentFactory {
|
|||||||
Path tempFile = createTempFile("pdf-stream-");
|
Path tempFile = createTempFile("pdf-stream-");
|
||||||
|
|
||||||
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
return loadAdaptivelyWithPassword(tempFile.toFile(), Files.size(tempFile), password);
|
PDDocument doc = loadAdaptivelyWithPassword(tempFile.toFile(), Files.size(tempFile), password);
|
||||||
|
if (!readOnly) {
|
||||||
|
postProcessDocument(doc);
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load from a file path string */
|
||||||
|
public PDDocument load(String path) throws IOException {
|
||||||
|
return load(path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load from a file path string with read-only option */
|
||||||
|
public PDDocument load(String path, boolean readOnly) throws IOException {
|
||||||
|
return load(new File(path), readOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load from a PDFFile object */
|
||||||
|
public PDDocument load(PDFFile pdfFile) throws IOException {
|
||||||
|
return load(pdfFile, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load from a PDFFile object with read-only option */
|
||||||
|
public PDDocument load(PDFFile pdfFile, boolean readOnly) throws IOException {
|
||||||
|
return load(pdfFile.getFileInput(), readOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load from a MultipartFile */
|
||||||
|
public PDDocument load(MultipartFile pdfFile) throws IOException {
|
||||||
|
return load(pdfFile, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load from a MultipartFile with read-only option */
|
||||||
|
public PDDocument load(MultipartFile pdfFile, boolean readOnly) throws IOException {
|
||||||
|
return load(pdfFile.getInputStream(), readOnly);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load with password from MultipartFile */
|
||||||
|
public PDDocument load(MultipartFile fileInput, String password) throws IOException {
|
||||||
|
return load(fileInput, password, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load with password from MultipartFile with read-only option */
|
||||||
|
public PDDocument load(MultipartFile fileInput, String password, boolean readOnly) throws IOException {
|
||||||
|
return load(fileInput.getInputStream(), password, readOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the appropriate caching strategy based on file size and available memory. This
|
* Determine the appropriate caching strategy based on file size and available memory. This
|
||||||
* common method is used by both password and non-password loading paths.
|
* common method is used by both password and non-password loading paths.
|
||||||
*/
|
*/
|
||||||
private StreamCacheCreateFunction getStreamCacheFunction(long contentSize) {
|
public StreamCacheCreateFunction getStreamCacheFunction(long contentSize) {
|
||||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||||
long freeMemory = Runtime.getRuntime().freeMemory();
|
long freeMemory = Runtime.getRuntime().freeMemory();
|
||||||
long totalMemory = Runtime.getRuntime().totalMemory();
|
long totalMemory = Runtime.getRuntime().totalMemory();
|
||||||
@ -197,8 +289,6 @@ public class CustomPDFDocumentFactory {
|
|||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Unsupported source type: " + source.getClass());
|
throw new IllegalArgumentException("Unsupported source type: " + source.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
postProcessDocument(document);
|
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,8 +310,6 @@ public class CustomPDFDocumentFactory {
|
|||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Unsupported source type: " + source.getClass());
|
throw new IllegalArgumentException("Unsupported source type: " + source.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
postProcessDocument(document);
|
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,23 +472,4 @@ public class CustomPDFDocumentFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load from a file path string */
|
|
||||||
public PDDocument load(String path) throws IOException {
|
|
||||||
return load(new File(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Load from a PDFFile object */
|
|
||||||
public PDDocument load(PDFFile pdfFile) throws IOException {
|
|
||||||
return load(pdfFile.getFileInput());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Load from a MultipartFile */
|
|
||||||
public PDDocument load(MultipartFile pdfFile) throws IOException {
|
|
||||||
return load(pdfFile.getInputStream());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Load with password from MultipartFile */
|
|
||||||
public PDDocument load(MultipartFile fileInput, String password) throws IOException {
|
|
||||||
return load(fileInput.getInputStream(), password);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -64,10 +64,19 @@ public class PdfMetadataService {
|
|||||||
|
|
||||||
String creator = stirlingPDFLabel;
|
String creator = stirlingPDFLabel;
|
||||||
|
|
||||||
if (applicationProperties.getEnterpriseEdition().getCustomMetadata().isAutoUpdateMetadata()
|
if (applicationProperties
|
||||||
|
.getPremium()
|
||||||
|
.getProFeatures()
|
||||||
|
.getCustomMetadata()
|
||||||
|
.isAutoUpdateMetadata()
|
||||||
&& runningEE) {
|
&& runningEE) {
|
||||||
|
|
||||||
creator = applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator();
|
creator =
|
||||||
|
applicationProperties
|
||||||
|
.getPremium()
|
||||||
|
.getProFeatures()
|
||||||
|
.getCustomMetadata()
|
||||||
|
.getCreator();
|
||||||
pdf.getDocumentInformation().setProducer(stirlingPDFLabel);
|
pdf.getDocumentInformation().setProducer(stirlingPDFLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,9 +93,18 @@ public class PdfMetadataService {
|
|||||||
pdf.getDocumentInformation().setModificationDate(Calendar.getInstance());
|
pdf.getDocumentInformation().setModificationDate(Calendar.getInstance());
|
||||||
|
|
||||||
String author = pdfMetadata.getAuthor();
|
String author = pdfMetadata.getAuthor();
|
||||||
if (applicationProperties.getEnterpriseEdition().getCustomMetadata().isAutoUpdateMetadata()
|
if (applicationProperties
|
||||||
|
.getPremium()
|
||||||
|
.getProFeatures()
|
||||||
|
.getCustomMetadata()
|
||||||
|
.isAutoUpdateMetadata()
|
||||||
&& runningEE) {
|
&& runningEE) {
|
||||||
author = applicationProperties.getEnterpriseEdition().getCustomMetadata().getAuthor();
|
author =
|
||||||
|
applicationProperties
|
||||||
|
.getPremium()
|
||||||
|
.getProFeatures()
|
||||||
|
.getCustomMetadata()
|
||||||
|
.getAuthor();
|
||||||
|
|
||||||
if (userService != null) {
|
if (userService != null) {
|
||||||
author = author.replace("username", userService.getCurrentUsername());
|
author = author.replace("username", userService.getCurrentUsername());
|
||||||
|
@ -334,27 +334,40 @@ public class PostHogService {
|
|||||||
addIfNotEmpty(
|
addIfNotEmpty(
|
||||||
properties,
|
properties,
|
||||||
"enterpriseEdition_enabled",
|
"enterpriseEdition_enabled",
|
||||||
applicationProperties.getEnterpriseEdition().isEnabled());
|
applicationProperties.getPremium().isEnabled());
|
||||||
if (applicationProperties.getEnterpriseEdition().isEnabled()) {
|
if (applicationProperties.getPremium().isEnabled()) {
|
||||||
addIfNotEmpty(
|
addIfNotEmpty(
|
||||||
properties,
|
properties,
|
||||||
"enterpriseEdition_customMetadata_autoUpdateMetadata",
|
"enterpriseEdition_customMetadata_autoUpdateMetadata",
|
||||||
applicationProperties
|
applicationProperties
|
||||||
.getEnterpriseEdition()
|
.getPremium()
|
||||||
|
.getProFeatures()
|
||||||
.getCustomMetadata()
|
.getCustomMetadata()
|
||||||
.isAutoUpdateMetadata());
|
.isAutoUpdateMetadata());
|
||||||
addIfNotEmpty(
|
addIfNotEmpty(
|
||||||
properties,
|
properties,
|
||||||
"enterpriseEdition_customMetadata_author",
|
"enterpriseEdition_customMetadata_author",
|
||||||
applicationProperties.getEnterpriseEdition().getCustomMetadata().getAuthor());
|
applicationProperties
|
||||||
|
.getPremium()
|
||||||
|
.getProFeatures()
|
||||||
|
.getCustomMetadata()
|
||||||
|
.getAuthor());
|
||||||
addIfNotEmpty(
|
addIfNotEmpty(
|
||||||
properties,
|
properties,
|
||||||
"enterpriseEdition_customMetadata_creator",
|
"enterpriseEdition_customMetadata_creator",
|
||||||
applicationProperties.getEnterpriseEdition().getCustomMetadata().getCreator());
|
applicationProperties
|
||||||
|
.getPremium()
|
||||||
|
.getProFeatures()
|
||||||
|
.getCustomMetadata()
|
||||||
|
.getCreator());
|
||||||
addIfNotEmpty(
|
addIfNotEmpty(
|
||||||
properties,
|
properties,
|
||||||
"enterpriseEdition_customMetadata_producer",
|
"enterpriseEdition_customMetadata_producer",
|
||||||
applicationProperties.getEnterpriseEdition().getCustomMetadata().getProducer());
|
applicationProperties
|
||||||
|
.getPremium()
|
||||||
|
.getProFeatures()
|
||||||
|
.getCustomMetadata()
|
||||||
|
.getProducer());
|
||||||
}
|
}
|
||||||
// Capture AutoPipeline properties
|
// Capture AutoPipeline properties
|
||||||
addIfNotEmpty(
|
addIfNotEmpty(
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=المستخدمين النشطين:
|
|||||||
adminUserSettings.disabledUsers=المستخدمين المعطلين:
|
adminUserSettings.disabledUsers=المستخدمين المعطلين:
|
||||||
adminUserSettings.totalUsers=إجمالي المستخدمين:
|
adminUserSettings.totalUsers=إجمالي المستخدمين:
|
||||||
adminUserSettings.lastRequest=آخر طلب
|
adminUserSettings.lastRequest=آخر طلب
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=استيراد/تصدير قاعدة البيانات
|
database.title=استيراد/تصدير قاعدة البيانات
|
||||||
database.header=استيراد/تصدير قاعدة البيانات
|
database.header=استيراد/تصدير قاعدة البيانات
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=تنظيف PDF
|
|||||||
sanitizePDF.header=تنظيف ملف PDF
|
sanitizePDF.header=تنظيف ملف PDF
|
||||||
sanitizePDF.selectText.1=إزالة إجراءات جافا سكريبت
|
sanitizePDF.selectText.1=إزالة إجراءات جافا سكريبت
|
||||||
sanitizePDF.selectText.2=إزالة الملفات المضمنة
|
sanitizePDF.selectText.2=إزالة الملفات المضمنة
|
||||||
sanitizePDF.selectText.3=إزالة البيانات الوصفية
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=إزالة الروابط
|
sanitizePDF.selectText.4=إزالة الروابط
|
||||||
sanitizePDF.selectText.5=إزالة الخطوط
|
sanitizePDF.selectText.5=إزالة الخطوط
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=تنظيف PDF
|
sanitizePDF.submit=تنظيف PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Aktiv İstifadəçilər:
|
|||||||
adminUserSettings.disabledUsers=Deaktiv İstifadəçilər:
|
adminUserSettings.disabledUsers=Deaktiv İstifadəçilər:
|
||||||
adminUserSettings.totalUsers=Ümumi İstifadəçilər:
|
adminUserSettings.totalUsers=Ümumi İstifadəçilər:
|
||||||
adminUserSettings.lastRequest=Son sorğu
|
adminUserSettings.lastRequest=Son sorğu
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Verilənlər bazasını Daxil/Xaric Et
|
database.title=Verilənlər bazasını Daxil/Xaric Et
|
||||||
database.header=Verilənlər bazasını Daxil/Xaric Et
|
database.header=Verilənlər bazasını Daxil/Xaric Et
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=PDF-i Təmizlə
|
|||||||
sanitizePDF.header=PDF Faylını Təmizlə
|
sanitizePDF.header=PDF Faylını Təmizlə
|
||||||
sanitizePDF.selectText.1=JavaScript Fəaliyyətlərini Sil
|
sanitizePDF.selectText.1=JavaScript Fəaliyyətlərini Sil
|
||||||
sanitizePDF.selectText.2=Daxil Edilmiş Faylları Sil
|
sanitizePDF.selectText.2=Daxil Edilmiş Faylları Sil
|
||||||
sanitizePDF.selectText.3=Metadatanı Sil
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Linkləri Sil
|
sanitizePDF.selectText.4=Linkləri Sil
|
||||||
sanitizePDF.selectText.5=Şriftləri Sil
|
sanitizePDF.selectText.5=Şriftləri Sil
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=PDF-i Təmizlə
|
sanitizePDF.submit=PDF-i Təmizlə
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Активни потребители:
|
|||||||
adminUserSettings.disabledUsers=Деактивирани потребители:
|
adminUserSettings.disabledUsers=Деактивирани потребители:
|
||||||
adminUserSettings.totalUsers=Общо потребители:
|
adminUserSettings.totalUsers=Общо потребители:
|
||||||
adminUserSettings.lastRequest=Последна заявка
|
adminUserSettings.lastRequest=Последна заявка
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Импорт/Експорт на база данни
|
database.title=Импорт/Експорт на база данни
|
||||||
database.header=Импорт/Експорт на база данни
|
database.header=Импорт/Експорт на база данни
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Дезинфектирай PDF
|
|||||||
sanitizePDF.header=Дезинфектира PDF файл
|
sanitizePDF.header=Дезинфектира PDF файл
|
||||||
sanitizePDF.selectText.1=Премахва JavaScript действия
|
sanitizePDF.selectText.1=Премахва JavaScript действия
|
||||||
sanitizePDF.selectText.2=Премахва вградени файлове
|
sanitizePDF.selectText.2=Премахва вградени файлове
|
||||||
sanitizePDF.selectText.3=Премахва метаданни
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Премахва линкове
|
sanitizePDF.selectText.4=Премахва линкове
|
||||||
sanitizePDF.selectText.5=Премахва шрифтове
|
sanitizePDF.selectText.5=Премахва шрифтове
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Дезинфектирай PDF
|
sanitizePDF.submit=Дезинфектирай PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Usuaris Actius:
|
|||||||
adminUserSettings.disabledUsers=Usuaris Deshabilitats:
|
adminUserSettings.disabledUsers=Usuaris Deshabilitats:
|
||||||
adminUserSettings.totalUsers=Total d'Usuaris:
|
adminUserSettings.totalUsers=Total d'Usuaris:
|
||||||
adminUserSettings.lastRequest=Darrera Sol·licitud
|
adminUserSettings.lastRequest=Darrera Sol·licitud
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Importació/Exportació de Base de Dades
|
database.title=Importació/Exportació de Base de Dades
|
||||||
database.header=Importació/Exportació de Base de Dades
|
database.header=Importació/Exportació de Base de Dades
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Neteja PDF
|
|||||||
sanitizePDF.header=Neteja un fitxer PDF
|
sanitizePDF.header=Neteja un fitxer PDF
|
||||||
sanitizePDF.selectText.1=Elimina accions JavaScript
|
sanitizePDF.selectText.1=Elimina accions JavaScript
|
||||||
sanitizePDF.selectText.2=Elimina fitxers incrustats
|
sanitizePDF.selectText.2=Elimina fitxers incrustats
|
||||||
sanitizePDF.selectText.3=Elimina metadades
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Elimina enllaços
|
sanitizePDF.selectText.4=Elimina enllaços
|
||||||
sanitizePDF.selectText.5=Elimina fonts
|
sanitizePDF.selectText.5=Elimina fonts
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Neteja PDF
|
sanitizePDF.submit=Neteja PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Aktivní uživatelé:
|
|||||||
adminUserSettings.disabledUsers=Deaktivovaní uživatelé:
|
adminUserSettings.disabledUsers=Deaktivovaní uživatelé:
|
||||||
adminUserSettings.totalUsers=Celkem uživatelů:
|
adminUserSettings.totalUsers=Celkem uživatelů:
|
||||||
adminUserSettings.lastRequest=Poslední požadavek
|
adminUserSettings.lastRequest=Poslední požadavek
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Import/Export databáze
|
database.title=Import/Export databáze
|
||||||
database.header=Import/Export databáze
|
database.header=Import/Export databáze
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Sanitizovat PDF
|
|||||||
sanitizePDF.header=Sanitizovat PDF soubor
|
sanitizePDF.header=Sanitizovat PDF soubor
|
||||||
sanitizePDF.selectText.1=Odstranit JavaScript akce
|
sanitizePDF.selectText.1=Odstranit JavaScript akce
|
||||||
sanitizePDF.selectText.2=Odstranit vložené soubory
|
sanitizePDF.selectText.2=Odstranit vložené soubory
|
||||||
sanitizePDF.selectText.3=Odstranit metadata
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Odstranit odkazy
|
sanitizePDF.selectText.4=Odstranit odkazy
|
||||||
sanitizePDF.selectText.5=Odstranit písma
|
sanitizePDF.selectText.5=Odstranit písma
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Sanitizovat PDF
|
sanitizePDF.submit=Sanitizovat PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Aktive Brugere:
|
|||||||
adminUserSettings.disabledUsers=Deaktiverede Brugere:
|
adminUserSettings.disabledUsers=Deaktiverede Brugere:
|
||||||
adminUserSettings.totalUsers=Samlet Antal Brugere:
|
adminUserSettings.totalUsers=Samlet Antal Brugere:
|
||||||
adminUserSettings.lastRequest=Seneste Anmodning
|
adminUserSettings.lastRequest=Seneste Anmodning
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Database Import/Eksport
|
database.title=Database Import/Eksport
|
||||||
database.header=Database Import/Eksport
|
database.header=Database Import/Eksport
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Rens PDF
|
|||||||
sanitizePDF.header=Rens en PDF-fil
|
sanitizePDF.header=Rens en PDF-fil
|
||||||
sanitizePDF.selectText.1=Fjern JavaScript-handlinger
|
sanitizePDF.selectText.1=Fjern JavaScript-handlinger
|
||||||
sanitizePDF.selectText.2=Fjern indlejrede filer
|
sanitizePDF.selectText.2=Fjern indlejrede filer
|
||||||
sanitizePDF.selectText.3=Fjern metadata
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Fjern links
|
sanitizePDF.selectText.4=Fjern links
|
||||||
sanitizePDF.selectText.5=Fjern skrifttyper
|
sanitizePDF.selectText.5=Fjern skrifttyper
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Rens PDF
|
sanitizePDF.submit=Rens PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Aktive Benutzer:
|
|||||||
adminUserSettings.disabledUsers=Deaktivierte Benutzer:
|
adminUserSettings.disabledUsers=Deaktivierte Benutzer:
|
||||||
adminUserSettings.totalUsers=Gesamtzahl der Benutzer:
|
adminUserSettings.totalUsers=Gesamtzahl der Benutzer:
|
||||||
adminUserSettings.lastRequest=Letzte Anfrage
|
adminUserSettings.lastRequest=Letzte Anfrage
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Datenbank Import/Export
|
database.title=Datenbank Import/Export
|
||||||
database.header=Datenbank Import/Export
|
database.header=Datenbank Import/Export
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=PDF Bereinigen
|
|||||||
sanitizePDF.header=PDF Bereinigen
|
sanitizePDF.header=PDF Bereinigen
|
||||||
sanitizePDF.selectText.1=Javascript-Aktionen entfernen
|
sanitizePDF.selectText.1=Javascript-Aktionen entfernen
|
||||||
sanitizePDF.selectText.2=Eingebettete Dateien entfernen
|
sanitizePDF.selectText.2=Eingebettete Dateien entfernen
|
||||||
sanitizePDF.selectText.3=Metadaten entfernen
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Links entfernen
|
sanitizePDF.selectText.4=Links entfernen
|
||||||
sanitizePDF.selectText.5=Schriftarten entfernen
|
sanitizePDF.selectText.5=Schriftarten entfernen
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Bereinigen
|
sanitizePDF.submit=Bereinigen
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Ενεργοί χρήστες:
|
|||||||
adminUserSettings.disabledUsers=Απενεργοποιημένοι χρήστες:
|
adminUserSettings.disabledUsers=Απενεργοποιημένοι χρήστες:
|
||||||
adminUserSettings.totalUsers=Συνολικοί χρήστες:
|
adminUserSettings.totalUsers=Συνολικοί χρήστες:
|
||||||
adminUserSettings.lastRequest=Τελευταίο αίτημα
|
adminUserSettings.lastRequest=Τελευταίο αίτημα
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Εισαγωγή/Εξαγωγή βάσης δεδομένων
|
database.title=Εισαγωγή/Εξαγωγή βάσης δεδομένων
|
||||||
database.header=Εισαγωγή/Εξαγωγή βάσης δεδομένων
|
database.header=Εισαγωγή/Εξαγωγή βάσης δεδομένων
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Εξυγίανση PDF
|
|||||||
sanitizePDF.header=Εξυγίανση αρχείου PDF
|
sanitizePDF.header=Εξυγίανση αρχείου PDF
|
||||||
sanitizePDF.selectText.1=Αφαίρεση ενεργειών JavaScript
|
sanitizePDF.selectText.1=Αφαίρεση ενεργειών JavaScript
|
||||||
sanitizePDF.selectText.2=Αφαίρεση ενσωματωμένων αρχείων
|
sanitizePDF.selectText.2=Αφαίρεση ενσωματωμένων αρχείων
|
||||||
sanitizePDF.selectText.3=Αφαίρεση μεταδεδομένων
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Αφαίρεση συνδέσμων
|
sanitizePDF.selectText.4=Αφαίρεση συνδέσμων
|
||||||
sanitizePDF.selectText.5=Αφαίρεση γραμματοσειρών
|
sanitizePDF.selectText.5=Αφαίρεση γραμματοσειρών
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Εξυγίανση PDF
|
sanitizePDF.submit=Εξυγίανση PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,33 +231,7 @@ adminUserSettings.activeUsers=Active Users:
|
|||||||
adminUserSettings.disabledUsers=Disabled Users:
|
adminUserSettings.disabledUsers=Disabled Users:
|
||||||
adminUserSettings.totalUsers=Total Users:
|
adminUserSettings.totalUsers=Total Users:
|
||||||
adminUserSettings.lastRequest=Last Request
|
adminUserSettings.lastRequest=Last Request
|
||||||
adminUserSettings.userSessions=User sessions
|
|
||||||
adminUserSettings.totalSessions=Total Sessions:
|
|
||||||
adminUserSettings.usage=View Usage
|
|
||||||
|
|
||||||
endpointStatistics.title=Endpoint Statistics
|
|
||||||
endpointStatistics.header=Endpoint Statistics
|
|
||||||
endpointStatistics.top10=Top 10
|
|
||||||
endpointStatistics.top20=Top 20
|
|
||||||
endpointStatistics.all=All
|
|
||||||
endpointStatistics.refresh=Refresh
|
|
||||||
endpointStatistics.includeHomepage=Include Homepage ('/')
|
|
||||||
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
|
||||||
endpointStatistics.totalEndpoints=Total Endpoints
|
|
||||||
endpointStatistics.totalVisits=Total Visits
|
|
||||||
endpointStatistics.showing=Showing
|
|
||||||
endpointStatistics.selectedVisits=Selected Visits
|
|
||||||
endpointStatistics.endpoint=Endpoint
|
|
||||||
endpointStatistics.visits=Visits
|
|
||||||
endpointStatistics.percentage=Percentage
|
|
||||||
endpointStatistics.loading=Loading...
|
|
||||||
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
|
||||||
endpointStatistics.home=Home
|
|
||||||
endpointStatistics.login=Login
|
|
||||||
endpointStatistics.top=Top
|
|
||||||
endpointStatistics.numberOfVisits=Number of Visits
|
|
||||||
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
|
||||||
endpointStatistics.retry=Retry
|
|
||||||
|
|
||||||
database.title=Database Import/Export
|
database.title=Database Import/Export
|
||||||
database.header=Database Import/Export
|
database.header=Database Import/Export
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Active Users:
|
|||||||
adminUserSettings.disabledUsers=Disabled Users:
|
adminUserSettings.disabledUsers=Disabled Users:
|
||||||
adminUserSettings.totalUsers=Total Users:
|
adminUserSettings.totalUsers=Total Users:
|
||||||
adminUserSettings.lastRequest=Last Request
|
adminUserSettings.lastRequest=Last Request
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Database Import/Export
|
database.title=Database Import/Export
|
||||||
database.header=Database Import/Export
|
database.header=Database Import/Export
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Sanitize PDF
|
|||||||
sanitizePDF.header=Sanitize a PDF file
|
sanitizePDF.header=Sanitize a PDF file
|
||||||
sanitizePDF.selectText.1=Remove JavaScript actions
|
sanitizePDF.selectText.1=Remove JavaScript actions
|
||||||
sanitizePDF.selectText.2=Remove embedded files
|
sanitizePDF.selectText.2=Remove embedded files
|
||||||
sanitizePDF.selectText.3=Remove metadata
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Remove links
|
sanitizePDF.selectText.4=Remove links
|
||||||
sanitizePDF.selectText.5=Remove fonts
|
sanitizePDF.selectText.5=Remove fonts
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Sanitize PDF
|
sanitizePDF.submit=Sanitize PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Usuarios Activos:
|
|||||||
adminUserSettings.disabledUsers=Usuarios deshabilitados:
|
adminUserSettings.disabledUsers=Usuarios deshabilitados:
|
||||||
adminUserSettings.totalUsers=Usuarios totales:
|
adminUserSettings.totalUsers=Usuarios totales:
|
||||||
adminUserSettings.lastRequest=Última petición
|
adminUserSettings.lastRequest=Última petición
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Base de Datos Importar/Exportar
|
database.title=Base de Datos Importar/Exportar
|
||||||
database.header=Base de Datos Importar/Exportar
|
database.header=Base de Datos Importar/Exportar
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Limpiar archivo PDF
|
|||||||
sanitizePDF.header=Limpiar un archivo PDF
|
sanitizePDF.header=Limpiar un archivo PDF
|
||||||
sanitizePDF.selectText.1=Eliminar código JavaScript
|
sanitizePDF.selectText.1=Eliminar código JavaScript
|
||||||
sanitizePDF.selectText.2=Eliminar archivos incrustados
|
sanitizePDF.selectText.2=Eliminar archivos incrustados
|
||||||
sanitizePDF.selectText.3=Eliminar metadatos
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Eliminar enlaces
|
sanitizePDF.selectText.4=Eliminar enlaces
|
||||||
sanitizePDF.selectText.5=Eliminar fuentes
|
sanitizePDF.selectText.5=Eliminar fuentes
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Limpiar PDF
|
sanitizePDF.submit=Limpiar PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Active Users:
|
|||||||
adminUserSettings.disabledUsers=Disabled Users:
|
adminUserSettings.disabledUsers=Disabled Users:
|
||||||
adminUserSettings.totalUsers=Total Users:
|
adminUserSettings.totalUsers=Total Users:
|
||||||
adminUserSettings.lastRequest=Last Request
|
adminUserSettings.lastRequest=Last Request
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Database Import/Export
|
database.title=Database Import/Export
|
||||||
database.header=Database Import/Export
|
database.header=Database Import/Export
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=PDF-a desinfektatu
|
|||||||
sanitizePDF.header=PDF fitxategi bat desinfektatu
|
sanitizePDF.header=PDF fitxategi bat desinfektatu
|
||||||
sanitizePDF.selectText.1=Ezabatu JavaScript akzioak
|
sanitizePDF.selectText.1=Ezabatu JavaScript akzioak
|
||||||
sanitizePDF.selectText.2=Ezabatu embedded fitxategiak
|
sanitizePDF.selectText.2=Ezabatu embedded fitxategiak
|
||||||
sanitizePDF.selectText.3=Ezabatu metadata
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Ezabatu esketak
|
sanitizePDF.selectText.4=Ezabatu esketak
|
||||||
sanitizePDF.selectText.5=Ezabatu iturri letrak
|
sanitizePDF.selectText.5=Ezabatu iturri letrak
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Desinfektatu PDF
|
sanitizePDF.submit=Desinfektatu PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=کاربران فعال:
|
|||||||
adminUserSettings.disabledUsers=کاربران غیرفعال:
|
adminUserSettings.disabledUsers=کاربران غیرفعال:
|
||||||
adminUserSettings.totalUsers=کل کاربران:
|
adminUserSettings.totalUsers=کل کاربران:
|
||||||
adminUserSettings.lastRequest=آخرین درخواست
|
adminUserSettings.lastRequest=آخرین درخواست
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=وارد کردن/صادر کردن پایگاه داده
|
database.title=وارد کردن/صادر کردن پایگاه داده
|
||||||
database.header=وارد کردن/صادر کردن پایگاه داده
|
database.header=وارد کردن/صادر کردن پایگاه داده
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=پاکسازی PDF
|
|||||||
sanitizePDF.header=پاکسازی یک فایل PDF
|
sanitizePDF.header=پاکسازی یک فایل PDF
|
||||||
sanitizePDF.selectText.1=حذف عملیات جاوااسکریپت
|
sanitizePDF.selectText.1=حذف عملیات جاوااسکریپت
|
||||||
sanitizePDF.selectText.2=حذف فایلهای جاسازی شده
|
sanitizePDF.selectText.2=حذف فایلهای جاسازی شده
|
||||||
sanitizePDF.selectText.3=حذف متادادهها
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=حذف لینکها
|
sanitizePDF.selectText.4=حذف لینکها
|
||||||
sanitizePDF.selectText.5=حذف فونتها
|
sanitizePDF.selectText.5=حذف فونتها
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=پاکسازی PDF
|
sanitizePDF.submit=پاکسازی PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Utilisateurs actifs :
|
|||||||
adminUserSettings.disabledUsers=Utilisateurs désactivés :
|
adminUserSettings.disabledUsers=Utilisateurs désactivés :
|
||||||
adminUserSettings.totalUsers=Utilisateurs au total :
|
adminUserSettings.totalUsers=Utilisateurs au total :
|
||||||
adminUserSettings.lastRequest=Dernière requête
|
adminUserSettings.lastRequest=Dernière requête
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Import/Export de la Base de Données
|
database.title=Import/Export de la Base de Données
|
||||||
database.header=Import/Export de la Base de Données
|
database.header=Import/Export de la Base de Données
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Assainir
|
|||||||
sanitizePDF.header=Assainir
|
sanitizePDF.header=Assainir
|
||||||
sanitizePDF.selectText.1=Supprimer les actions JavaScript
|
sanitizePDF.selectText.1=Supprimer les actions JavaScript
|
||||||
sanitizePDF.selectText.2=Supprimer les fichiers intégrés
|
sanitizePDF.selectText.2=Supprimer les fichiers intégrés
|
||||||
sanitizePDF.selectText.3=Supprimer les métadonnées
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Supprimer les liens
|
sanitizePDF.selectText.4=Supprimer les liens
|
||||||
sanitizePDF.selectText.5=Supprimer les polices
|
sanitizePDF.selectText.5=Supprimer les polices
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Assainir
|
sanitizePDF.submit=Assainir
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Úsáideoirí Gníomhacha:
|
|||||||
adminUserSettings.disabledUsers=Úsáideoirí faoi mhíchumas:
|
adminUserSettings.disabledUsers=Úsáideoirí faoi mhíchumas:
|
||||||
adminUserSettings.totalUsers=Úsáideoirí Iomlán:
|
adminUserSettings.totalUsers=Úsáideoirí Iomlán:
|
||||||
adminUserSettings.lastRequest=Iarratas Deiridh
|
adminUserSettings.lastRequest=Iarratas Deiridh
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Iompórtáil/Easpórtáil Bunachar Sonraí
|
database.title=Iompórtáil/Easpórtáil Bunachar Sonraí
|
||||||
database.header=Iompórtáil/Easpórtáil Bunachar Sonraí
|
database.header=Iompórtáil/Easpórtáil Bunachar Sonraí
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=PDF sláintíocht
|
|||||||
sanitizePDF.header=Glanadh comhad PDF
|
sanitizePDF.header=Glanadh comhad PDF
|
||||||
sanitizePDF.selectText.1=Bain gníomhartha JavaScript
|
sanitizePDF.selectText.1=Bain gníomhartha JavaScript
|
||||||
sanitizePDF.selectText.2=Bain comhaid leabaithe
|
sanitizePDF.selectText.2=Bain comhaid leabaithe
|
||||||
sanitizePDF.selectText.3=Bain meiteashonraí
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Bain naisc
|
sanitizePDF.selectText.4=Bain naisc
|
||||||
sanitizePDF.selectText.5=Bain clónna
|
sanitizePDF.selectText.5=Bain clónna
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=PDF sláintíocht
|
sanitizePDF.submit=PDF sláintíocht
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=सक्रिय उपयोगकर्ता:
|
|||||||
adminUserSettings.disabledUsers=अक्षम उपयोगकर्ता:
|
adminUserSettings.disabledUsers=अक्षम उपयोगकर्ता:
|
||||||
adminUserSettings.totalUsers=कुल उपयोगकर्ता:
|
adminUserSettings.totalUsers=कुल उपयोगकर्ता:
|
||||||
adminUserSettings.lastRequest=अंतिम अनुरोध
|
adminUserSettings.lastRequest=अंतिम अनुरोध
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=डेटाबेस आयात/निर्यात
|
database.title=डेटाबेस आयात/निर्यात
|
||||||
database.header=डेटाबेस आयात/निर्यात
|
database.header=डेटाबेस आयात/निर्यात
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=PDF सैनिटाइज़ करें
|
|||||||
sanitizePDF.header=PDF फ़ाइल सैनिटाइज़ करें
|
sanitizePDF.header=PDF फ़ाइल सैनिटाइज़ करें
|
||||||
sanitizePDF.selectText.1=जावास्क्रिप्ट क्रियाएं हटाएं
|
sanitizePDF.selectText.1=जावास्क्रिप्ट क्रियाएं हटाएं
|
||||||
sanitizePDF.selectText.2=एम्बेडेड फ़ाइलें हटाएं
|
sanitizePDF.selectText.2=एम्बेडेड फ़ाइलें हटाएं
|
||||||
sanitizePDF.selectText.3=मेटाडेटा हटाएं
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=लिंक हटाएं
|
sanitizePDF.selectText.4=लिंक हटाएं
|
||||||
sanitizePDF.selectText.5=फ़ॉन्ट्स हटाएं
|
sanitizePDF.selectText.5=फ़ॉन्ट्स हटाएं
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=PDF सैनिटाइज़ करें
|
sanitizePDF.submit=PDF सैनिटाइज़ करें
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Aktivni korisnici:
|
|||||||
adminUserSettings.disabledUsers=Isključeni korisnici:
|
adminUserSettings.disabledUsers=Isključeni korisnici:
|
||||||
adminUserSettings.totalUsers=Ukupan broj korisnika:
|
adminUserSettings.totalUsers=Ukupan broj korisnika:
|
||||||
adminUserSettings.lastRequest=Zadnji zahtjev
|
adminUserSettings.lastRequest=Zadnji zahtjev
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Database Import/Export
|
database.title=Database Import/Export
|
||||||
database.header=Database Import/Export
|
database.header=Database Import/Export
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Sanirajte PDF
|
|||||||
sanitizePDF.header=Sanirajte PDF datoteku
|
sanitizePDF.header=Sanirajte PDF datoteku
|
||||||
sanitizePDF.selectText.1=Ukloni JavaScript akcije
|
sanitizePDF.selectText.1=Ukloni JavaScript akcije
|
||||||
sanitizePDF.selectText.2=Ukloni ugrađene datoteke
|
sanitizePDF.selectText.2=Ukloni ugrađene datoteke
|
||||||
sanitizePDF.selectText.3=Ukloni metapodatke
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Ukloni poveznice
|
sanitizePDF.selectText.4=Ukloni poveznice
|
||||||
sanitizePDF.selectText.5=Uklonite fontove
|
sanitizePDF.selectText.5=Uklonite fontove
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Sanirajte PDF
|
sanitizePDF.submit=Sanirajte PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Aktív felhasználók:
|
|||||||
adminUserSettings.disabledUsers=Letiltott felhasználók:
|
adminUserSettings.disabledUsers=Letiltott felhasználók:
|
||||||
adminUserSettings.totalUsers=Összes felhasználó:
|
adminUserSettings.totalUsers=Összes felhasználó:
|
||||||
adminUserSettings.lastRequest=Utolsó kérés
|
adminUserSettings.lastRequest=Utolsó kérés
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Adatbázis importálás/exportálás
|
database.title=Adatbázis importálás/exportálás
|
||||||
database.header=Adatbázis importálás/exportálás
|
database.header=Adatbázis importálás/exportálás
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=PDF tisztítása
|
|||||||
sanitizePDF.header=PDF fájl tisztítása
|
sanitizePDF.header=PDF fájl tisztítása
|
||||||
sanitizePDF.selectText.1=JavaScript műveletek eltávolítása
|
sanitizePDF.selectText.1=JavaScript műveletek eltávolítása
|
||||||
sanitizePDF.selectText.2=Beágyazott fájlok eltávolítása
|
sanitizePDF.selectText.2=Beágyazott fájlok eltávolítása
|
||||||
sanitizePDF.selectText.3=Metaadatok eltávolítása
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Hivatkozások eltávolítása
|
sanitizePDF.selectText.4=Hivatkozások eltávolítása
|
||||||
sanitizePDF.selectText.5=Betűtípusok eltávolítása
|
sanitizePDF.selectText.5=Betűtípusok eltávolítása
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=PDF tisztítása
|
sanitizePDF.submit=PDF tisztítása
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Pengguna Aktif:
|
|||||||
adminUserSettings.disabledUsers=Pengguna Dinonaktifkan:
|
adminUserSettings.disabledUsers=Pengguna Dinonaktifkan:
|
||||||
adminUserSettings.totalUsers=Total Pengguna:
|
adminUserSettings.totalUsers=Total Pengguna:
|
||||||
adminUserSettings.lastRequest=Permintaan Terakhir
|
adminUserSettings.lastRequest=Permintaan Terakhir
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Impor/Ekspor Database
|
database.title=Impor/Ekspor Database
|
||||||
database.header=Impor/Ekspor Database
|
database.header=Impor/Ekspor Database
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Bersihkan PDF
|
|||||||
sanitizePDF.header=Membersihkan berkas PDF
|
sanitizePDF.header=Membersihkan berkas PDF
|
||||||
sanitizePDF.selectText.1=Hapus tindakan JavaScript
|
sanitizePDF.selectText.1=Hapus tindakan JavaScript
|
||||||
sanitizePDF.selectText.2=Hapus berkas yang disematkan
|
sanitizePDF.selectText.2=Hapus berkas yang disematkan
|
||||||
sanitizePDF.selectText.3=Hapus metadata
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Hapus tautan
|
sanitizePDF.selectText.4=Hapus tautan
|
||||||
sanitizePDF.selectText.5=Hapus font
|
sanitizePDF.selectText.5=Hapus font
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Membersihkan PDF
|
sanitizePDF.submit=Membersihkan PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Utenti attivi:
|
|||||||
adminUserSettings.disabledUsers=Utenti disabili:
|
adminUserSettings.disabledUsers=Utenti disabili:
|
||||||
adminUserSettings.totalUsers=Utenti totali:
|
adminUserSettings.totalUsers=Utenti totali:
|
||||||
adminUserSettings.lastRequest=Ultima richiesta
|
adminUserSettings.lastRequest=Ultima richiesta
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Importazione/Esportazione database
|
database.title=Importazione/Esportazione database
|
||||||
database.header=Importazione/esportazione database
|
database.header=Importazione/esportazione database
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Pulire PDF
|
|||||||
sanitizePDF.header=Pulisci un file PDF
|
sanitizePDF.header=Pulisci un file PDF
|
||||||
sanitizePDF.selectText.1=Rimuovi le azioni JavaScript
|
sanitizePDF.selectText.1=Rimuovi le azioni JavaScript
|
||||||
sanitizePDF.selectText.2=Rimuovi i file incorporati
|
sanitizePDF.selectText.2=Rimuovi i file incorporati
|
||||||
sanitizePDF.selectText.3=Rimuovi i metadati
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Rimuovi collegamenti
|
sanitizePDF.selectText.4=Rimuovi collegamenti
|
||||||
sanitizePDF.selectText.5=Rimuovi i font
|
sanitizePDF.selectText.5=Rimuovi i font
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Pulisci PDF
|
sanitizePDF.submit=Pulisci PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=アクティブユーザー:
|
|||||||
adminUserSettings.disabledUsers=無効なユーザー:
|
adminUserSettings.disabledUsers=無効なユーザー:
|
||||||
adminUserSettings.totalUsers=ユーザー合計:
|
adminUserSettings.totalUsers=ユーザー合計:
|
||||||
adminUserSettings.lastRequest=最後のリクエスト
|
adminUserSettings.lastRequest=最後のリクエスト
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=データベースのインポート/エクスポート
|
database.title=データベースのインポート/エクスポート
|
||||||
database.header=データベースのインポート/エクスポート
|
database.header=データベースのインポート/エクスポート
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=PDFをサニタイズ
|
|||||||
sanitizePDF.header=PDFファイルをサニタイズ
|
sanitizePDF.header=PDFファイルをサニタイズ
|
||||||
sanitizePDF.selectText.1=JavaScriptアクションを削除
|
sanitizePDF.selectText.1=JavaScriptアクションを削除
|
||||||
sanitizePDF.selectText.2=埋め込みファイルを削除
|
sanitizePDF.selectText.2=埋め込みファイルを削除
|
||||||
sanitizePDF.selectText.3=メタデータを削除
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=リンクを削除
|
sanitizePDF.selectText.4=リンクを削除
|
||||||
sanitizePDF.selectText.5=フォントを削除
|
sanitizePDF.selectText.5=フォントを削除
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=PDFをサニタイズする
|
sanitizePDF.submit=PDFをサニタイズする
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=활성 사용자:
|
|||||||
adminUserSettings.disabledUsers=비활성화된 사용자:
|
adminUserSettings.disabledUsers=비활성화된 사용자:
|
||||||
adminUserSettings.totalUsers=전체 사용자:
|
adminUserSettings.totalUsers=전체 사용자:
|
||||||
adminUserSettings.lastRequest=마지막 요청
|
adminUserSettings.lastRequest=마지막 요청
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=데이터베이스 가져오기/내보내기
|
database.title=데이터베이스 가져오기/내보내기
|
||||||
database.header=데이터베이스 가져오기/내보내기
|
database.header=데이터베이스 가져오기/내보내기
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=PDF 정리
|
|||||||
sanitizePDF.header=PDF 파일 정리
|
sanitizePDF.header=PDF 파일 정리
|
||||||
sanitizePDF.selectText.1=JavaScript 작업 제거
|
sanitizePDF.selectText.1=JavaScript 작업 제거
|
||||||
sanitizePDF.selectText.2=임베디드 파일 제거
|
sanitizePDF.selectText.2=임베디드 파일 제거
|
||||||
sanitizePDF.selectText.3=메타데이터 제거
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=링크 제거
|
sanitizePDF.selectText.4=링크 제거
|
||||||
sanitizePDF.selectText.5=글꼴 제거
|
sanitizePDF.selectText.5=글꼴 제거
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=PDF 정리
|
sanitizePDF.submit=PDF 정리
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Active Users:
|
|||||||
adminUserSettings.disabledUsers=Disabled Users:
|
adminUserSettings.disabledUsers=Disabled Users:
|
||||||
adminUserSettings.totalUsers=Total Users:
|
adminUserSettings.totalUsers=Total Users:
|
||||||
adminUserSettings.lastRequest=Laatste aanvraag
|
adminUserSettings.lastRequest=Laatste aanvraag
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Database Importeer/Exporteer
|
database.title=Database Importeer/Exporteer
|
||||||
database.header=Database Importeer/Exporteer
|
database.header=Database Importeer/Exporteer
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=PDF opschonen
|
|||||||
sanitizePDF.header=Een PDF-bestand opschonen
|
sanitizePDF.header=Een PDF-bestand opschonen
|
||||||
sanitizePDF.selectText.1=Verwijder Javascript-acties
|
sanitizePDF.selectText.1=Verwijder Javascript-acties
|
||||||
sanitizePDF.selectText.2=Verwijder ingebedde bestanden
|
sanitizePDF.selectText.2=Verwijder ingebedde bestanden
|
||||||
sanitizePDF.selectText.3=Verwijder metadata
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Verwijder links
|
sanitizePDF.selectText.4=Verwijder links
|
||||||
sanitizePDF.selectText.5=Verwijder lettertypen
|
sanitizePDF.selectText.5=Verwijder lettertypen
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=PDF opschonen
|
sanitizePDF.submit=PDF opschonen
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
###########
|
###########
|
||||||
# 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
|
||||||
addPageNumbers.fontSize=Font Size
|
addPageNumbers.fontSize=Skriftstørrelse
|
||||||
addPageNumbers.fontName=Font Name
|
addPageNumbers.fontName=Skrifttype
|
||||||
pdfPrompt=Velg PDF(er)
|
pdfPrompt=Velg PDF(er)
|
||||||
multiPdfPrompt=Velg PDF-filer (2+)
|
multiPdfPrompt=Velg PDF-filer (2+)
|
||||||
multiPdfDropPrompt=Velg (eller dra og slipp) alle PDF-ene du trenger
|
multiPdfDropPrompt=Velg (eller dra og slipp) alle PDF-ene du trenger
|
||||||
@ -29,7 +29,7 @@ downloadPdf=Last ned PDF
|
|||||||
text=Tekst
|
text=Tekst
|
||||||
font=Skrifttype
|
font=Skrifttype
|
||||||
selectFillter=-- Velg --
|
selectFillter=-- Velg --
|
||||||
pageNum=Sidnummer
|
pageNum=Sidenummer
|
||||||
sizes.small=Liten
|
sizes.small=Liten
|
||||||
sizes.medium=Middels
|
sizes.medium=Middels
|
||||||
sizes.large=Stor
|
sizes.large=Stor
|
||||||
@ -56,12 +56,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.
|
invalidPasswordMessage=Passordet kan ikke være tomt og må ikke ha mellomrom i begynnelsen eller slutten.
|
||||||
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
|
disabledCurrentUserMessage=Den pålogga brukeren kan ikke deaktiveres.
|
||||||
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.
|
||||||
@ -77,18 +77,18 @@ color=Farge
|
|||||||
sponsor=Sponsor
|
sponsor=Sponsor
|
||||||
info=Info
|
info=Info
|
||||||
pro=Pro
|
pro=Pro
|
||||||
page=Page
|
page=Side
|
||||||
pages=Pages
|
pages=Sider
|
||||||
loading=Loading...
|
loading=Laster...
|
||||||
addToDoc=Add to Document
|
addToDoc=Legg til i dokument
|
||||||
reset=Reset
|
reset=Reset
|
||||||
apply=Apply
|
apply=Apply
|
||||||
|
|
||||||
legal.privacy=Privacy Policy
|
legal.privacy=Personvernerklæring
|
||||||
legal.terms=Terms and Conditions
|
legal.terms=Vilkår og betingelser
|
||||||
legal.accessibility=Accessibility
|
legal.accessibility=Tilgjengelighet
|
||||||
legal.cookie=Cookie Policy
|
legal.cookie=Informasjonskapsler
|
||||||
legal.impressum=Impressum
|
legal.impressum=Juridisk informasjon
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Pipeline #
|
# Pipeline #
|
||||||
@ -100,7 +100,7 @@ pipeline.defaultOption=Tilpasset
|
|||||||
pipeline.submitButton=Send inn
|
pipeline.submitButton=Send inn
|
||||||
pipeline.help=Pipeline hjelp
|
pipeline.help=Pipeline hjelp
|
||||||
pipeline.scanHelp=Mappe skanning hjelp
|
pipeline.scanHelp=Mappe skanning hjelp
|
||||||
pipeline.deletePrompt=Are you sure you want to delete pipeline
|
pipeline.deletePrompt=Er du sikker på at du vil slette denne pipelinen?
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# Pipeline Options #
|
# Pipeline Options #
|
||||||
@ -118,21 +118,21 @@ pipelineOptions.validateButton=Valider
|
|||||||
########################
|
########################
|
||||||
# ENTERPRISE EDITION #
|
# ENTERPRISE EDITION #
|
||||||
########################
|
########################
|
||||||
enterpriseEdition.button=Upgrade to Pro
|
enterpriseEdition.button=Oppgrader til Pro
|
||||||
enterpriseEdition.warning=This feature is only available to Pro users.
|
enterpriseEdition.warning=Denne funksjonen er kun tilgjengelig for Pro-brukere.
|
||||||
enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features.
|
enterpriseEdition.yamlAdvert=Stirling PDF Pro støtter YAML-konfigurasjons filer og andre SSO funksjoner.
|
||||||
enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro
|
enterpriseEdition.ssoAdvert=Søker du etter flere administrerings funksjoner? Sjekk ut Stirling PDF Pro
|
||||||
|
|
||||||
|
|
||||||
#################
|
#################
|
||||||
# Analytics #
|
# Analytics #
|
||||||
#################
|
#################
|
||||||
analytics.title=Do you want make Stirling PDF better?
|
analytics.title=Vill du gjøre Stirling PDF bedre?
|
||||||
analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.
|
analytics.paragraph1=Stirling PDF har valgfri analyse for å hjelpe oss med å forbedre produktet. Vi sporer ikke personlig informasjon eller filinnhold.
|
||||||
analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.
|
analytics.paragraph2=Vennligst vurder å aktivere analyse for å hjelpe Stirling-PDF å vokse og for å la oss forstå brukerne våre bedre.
|
||||||
analytics.enable=Enable analytics
|
analytics.enable=Aktiver analyse
|
||||||
analytics.disable=Disable analytics
|
analytics.disable=Deaktiver analyse
|
||||||
analytics.settings=You can change the settings for analytics in the config/settings.yml file
|
analytics.settings=Du kan endre innstillingene for analyse i config/settings.yml filen
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# NAVBAR #
|
# NAVBAR #
|
||||||
@ -144,14 +144,14 @@ navbar.language=Språk
|
|||||||
navbar.settings=Innstillinger
|
navbar.settings=Innstillinger
|
||||||
navbar.allTools=Verktøy
|
navbar.allTools=Verktøy
|
||||||
navbar.multiTool=Multi Verktøy
|
navbar.multiTool=Multi Verktøy
|
||||||
navbar.search=Search
|
navbar.search=Søk
|
||||||
navbar.sections.organize=Organisere
|
navbar.sections.organize=Organisere
|
||||||
navbar.sections.convertTo=Konverter til PDF
|
navbar.sections.convertTo=Konverter til PDF
|
||||||
navbar.sections.convertFrom=Konverter fra PDF
|
navbar.sections.convertFrom=Konverter fra PDF
|
||||||
navbar.sections.security=Signer & Sikkerhet
|
navbar.sections.security=Signer & Sikkerhet
|
||||||
navbar.sections.advance=Avansert
|
navbar.sections.advance=Avansert
|
||||||
navbar.sections.edit=Vis & Rediger
|
navbar.sections.edit=Vis & Rediger
|
||||||
navbar.sections.popular=Popular
|
navbar.sections.popular=Populært
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# SETTINGS #
|
# SETTINGS #
|
||||||
@ -210,7 +210,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.confirmChangeUserStatus=Skal brukeren deaktiveres/aktiveres?
|
||||||
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
|
||||||
@ -224,14 +224,38 @@ 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.editOwnProfil=Rediger din profil
|
||||||
adminUserSettings.enabledUser=enabled user
|
adminUserSettings.enabledUser=aktivert bruker
|
||||||
adminUserSettings.disabledUser=disabled user
|
adminUserSettings.disabledUser=deaktivert bruker
|
||||||
adminUserSettings.activeUsers=Active Users:
|
adminUserSettings.activeUsers=Aktive brukere:
|
||||||
adminUserSettings.disabledUsers=Disabled Users:
|
adminUserSettings.disabledUsers=Deaktiverte brukere:
|
||||||
adminUserSettings.totalUsers=Total Users:
|
adminUserSettings.totalUsers=Totalt antall brukere:
|
||||||
adminUserSettings.lastRequest=Last Request
|
adminUserSettings.lastRequest=Siste spørring
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Database Import/Eksport
|
database.title=Database Import/Eksport
|
||||||
database.header=Database Import/Eksport
|
database.header=Database Import/Eksport
|
||||||
@ -240,20 +264,20 @@ database.creationDate=Opprettelsesdato
|
|||||||
database.fileSize=Filstørrelse
|
database.fileSize=Filstørrelse
|
||||||
database.deleteBackupFile=Slett sikkerhetskopifil
|
database.deleteBackupFile=Slett sikkerhetskopifil
|
||||||
database.importBackupFile=Importer sikkerhetskopifil
|
database.importBackupFile=Importer sikkerhetskopifil
|
||||||
database.createBackupFile=Create Backup File
|
database.createBackupFile=Lag sikkerhetskopifil
|
||||||
database.downloadBackupFile=Last ned sikkerhetskopifil
|
database.downloadBackupFile=Last ned sikkerhetskopifil
|
||||||
database.info_1=Når du importerer data, er det avgjørende å sikre riktig struktur. Hvis du er usikker på hva du gjør, bør du søke råd og støtte fra en profesjonell. En feil i strukturen kan føre til applikasjonsfeil, inkludert fullstendig manglende evne til å kjøre applikasjonen.
|
database.info_1=Når du importerer data, er det avgjørende å sikre riktig struktur. Hvis du er usikker på hva du gjør, bør du søke råd og støtte fra en profesjonell. En feil i strukturen kan føre til applikasjonsfeil, inkludert fullstendig manglende evne til å kjøre applikasjonen.
|
||||||
database.info_2=Filnavnet spiller ingen rolle ved opplasting. Det vil bli omdøpt etterpå for å følge formatet backup_user_yyyyMMddHHmm.sql, for å sikre en konsekvent navnekonvensjon.
|
database.info_2=Filnavnet spiller ingen rolle ved opplasting. Det vil bli omdøpt etterpå for å følge formatet backup_user_yyyyMMddHHmm.sql, for å sikre en konsekvent navnekonvensjon.
|
||||||
database.submit=Importer sikkerhetskopi
|
database.submit=Importer sikkerhetskopi
|
||||||
database.importIntoDatabaseSuccessed=Import til database vellykket
|
database.importIntoDatabaseSuccessed=Import til database vellykket
|
||||||
database.backupCreated=Database backup successful
|
database.backupCreated=Sikkerhetskopiering opprettet
|
||||||
database.fileNotFound=Fil ikke funnet
|
database.fileNotFound=Fil ikke funnet
|
||||||
database.fileNullOrEmpty=Fil må ikke være tom eller null
|
database.fileNullOrEmpty=Fil må ikke være tom eller null
|
||||||
database.failedImportFile=Import av fil mislyktes
|
database.failedImportFile=Import av fil mislyktes
|
||||||
database.notSupported=This function is not available for your database connection.
|
database.notSupported=Denne funksjonen er ikke tilgjengelig for din databasetilkobling.
|
||||||
|
|
||||||
session.expired=Your session has expired. Please refresh the page and try again.
|
session.expired=Økten din har utløpt. Vennligst oppdater siden og prøv igjen.
|
||||||
session.refreshPage=Refresh Page
|
session.refreshPage=Oppdater Side
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# HOME-PAGE #
|
# HOME-PAGE #
|
||||||
@ -266,14 +290,14 @@ home.viewPdf.title=View/Edit PDF
|
|||||||
home.viewPdf.desc=Vis, annoter, legg til tekst eller bilder
|
home.viewPdf.desc=Vis, annoter, legg til tekst eller bilder
|
||||||
viewPdf.tags=vis,les,annoter,tekst,bilde
|
viewPdf.tags=vis,les,annoter,tekst,bilde
|
||||||
|
|
||||||
home.setFavorites=Set Favourites
|
home.setFavorites=Angi Favoritter
|
||||||
home.hideFavorites=Hide Favourites
|
home.hideFavorites=Skjul Favoritter
|
||||||
home.showFavorites=Show Favourites
|
home.showFavorites=Vis Favoritter
|
||||||
home.legacyHomepage=Old homepage
|
home.legacyHomepage=Gammel hjemmeside
|
||||||
home.newHomePage=Try our new homepage!
|
home.newHomePage=Prøv vår nye hjemmeside!
|
||||||
home.alphabetical=Alphabetical
|
home.alphabetical=Alfabetisk
|
||||||
home.globalPopularity=Global Popularity
|
home.globalPopularity=Global Popularitet
|
||||||
home.sortBy=Sort by:
|
home.sortBy=Sorter etter:
|
||||||
|
|
||||||
home.multiTool.title=PDF Multi Verktøy
|
home.multiTool.title=PDF Multi Verktøy
|
||||||
home.multiTool.desc=Slå sammen, roter, omorganiser og fjern sider
|
home.multiTool.desc=Slå sammen, roter, omorganiser og fjern sider
|
||||||
@ -489,9 +513,9 @@ home.autoRedact.title=Automatisk Sensurering
|
|||||||
home.autoRedact.desc=Automatisk sensurering (sverter ut) tekst i en PDF basert på inntastet tekst
|
home.autoRedact.desc=Automatisk sensurering (sverter ut) tekst i en PDF basert på inntastet tekst
|
||||||
autoRedact.tags=Sensurere,Skjule,sverte ut,svart,markør,skjult
|
autoRedact.tags=Sensurere,Skjule,sverte ut,svart,markør,skjult
|
||||||
|
|
||||||
home.redact.title=Manual Redaction
|
home.redact.title=Manuell Sensurering
|
||||||
home.redact.desc=Redacts a PDF based on selected text, drawn shapes and/or selected page(s)
|
home.redact.desc=Sensurerer en PDF basert på valgt tekst, tegnede former og/eller valgte side(r)
|
||||||
redact.tags=Redact,Hide,black out,black,marker,hidden,manual
|
redact.tags=Sensurere,Skjule,sverte ut,svart,markør,skjult,manuell
|
||||||
|
|
||||||
home.tableExtraxt.title=PDF til CSV
|
home.tableExtraxt.title=PDF til CSV
|
||||||
home.tableExtraxt.desc=Ekstraherer tabeller fra en PDF og konverterer dem til CSV
|
home.tableExtraxt.desc=Ekstraherer tabeller fra en PDF og konverterer dem til CSV
|
||||||
@ -516,37 +540,37 @@ home.AddStampRequest.desc=Legg til tekst eller bilde stempler på angitte steder
|
|||||||
AddStampRequest.tags=stempel,legg til bilde,senter bilde,vannmerke,PDF,embed,tilpass
|
AddStampRequest.tags=stempel,legg til bilde,senter bilde,vannmerke,PDF,embed,tilpass
|
||||||
|
|
||||||
|
|
||||||
home.removeImagePdf.title=Remove image
|
home.removeImagePdf.title=Fjern bilde
|
||||||
home.removeImagePdf.desc=Remove image from PDF to reduce file size
|
home.removeImagePdf.desc=Fjern bilde fra PDF for å redusere filstørrelsen
|
||||||
removeImagePdf.tags=Remove Image,Page operations,Back end,server side
|
removeImagePdf.tags=Fjern Bilde,Sideoperasjoner,Backend,serverside
|
||||||
|
|
||||||
|
|
||||||
home.splitPdfByChapters.title=Split PDF by Chapters
|
home.splitPdfByChapters.title=Split PDF by Chapters
|
||||||
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
|
home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure.
|
||||||
splitPdfByChapters.tags=split,chapters,bookmarks,organize
|
splitPdfByChapters.tags=split,chapters,bookmarks,organize
|
||||||
|
|
||||||
home.validateSignature.title=Validate PDF Signature
|
home.validateSignature.title=Valider PDF-signatur
|
||||||
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
|
home.validateSignature.desc=Verifiser digitale signaturer og sertifikater i PDF-dokumenter
|
||||||
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
|
validateSignature.tags=signatur,verifiser,valider,pdf,sertifikat,digital signatur,Valider signatur,Valider sertifikat
|
||||||
|
|
||||||
#replace-invert-color
|
#replace-invert-color
|
||||||
replace-color.title=Replace-Invert-Color
|
replace-color.title=Erstatt-Inverter-Farge
|
||||||
replace-color.header=Replace-Invert Color PDF
|
replace-color.header=Erstatt-Inverter Farge PDF
|
||||||
home.replaceColorPdf.title=Replace and Invert Color
|
home.replaceColorPdf.title=Erstatt og Inverter Farge
|
||||||
home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size
|
home.replaceColorPdf.desc=Erstatt farge for tekst og bakgrunn i PDF og inverter full farge av pdf for å redusere filstørrelsen
|
||||||
replaceColorPdf.tags=Replace Color,Page operations,Back end,server side
|
replaceColorPdf.tags=Erstatt Farge,Sideoperasjoner,Backend,serverside
|
||||||
replace-color.selectText.1=Replace or Invert color Options
|
replace-color.selectText.1=Erstatt eller Inverter farge alternativer
|
||||||
replace-color.selectText.2=Default(Default high contrast colors)
|
replace-color.selectText.2=Standard(Standard høy kontrast farger)
|
||||||
replace-color.selectText.3=Custom(Customized colors)
|
replace-color.selectText.3=Tilpasset(Tilpassede farger)
|
||||||
replace-color.selectText.4=Full-Invert(Invert all colors)
|
replace-color.selectText.4=Full-Invertering(Inverter alle farger)
|
||||||
replace-color.selectText.5=High contrast color options
|
replace-color.selectText.5=Høy kontrast fargealternativer
|
||||||
replace-color.selectText.6=white text on black background
|
replace-color.selectText.6=hvit tekst på svart bakgrunn
|
||||||
replace-color.selectText.7=Black text on white background
|
replace-color.selectText.7=Svart tekst på hvit bakgrunn
|
||||||
replace-color.selectText.8=Yellow text on black background
|
replace-color.selectText.8=Gul tekst på svart bakgrunn
|
||||||
replace-color.selectText.9=Green text on black background
|
replace-color.selectText.9=Grønn tekst på svart bakgrunn
|
||||||
replace-color.selectText.10=Choose text Color
|
replace-color.selectText.10=Velg tekstfarge
|
||||||
replace-color.selectText.11=Choose background Color
|
replace-color.selectText.11=Velg bakgrunnsfarge
|
||||||
replace-color.submit=Replace
|
replace-color.submit=Erstatt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -565,18 +589,18 @@ 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.oAuth2AdminBlockedUser=Registrering eller pålogging for ikke-registrerte brukere er for øyeblikket blokkert. Vennligst kontakt 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.relyingPartyRegistrationNotFound=No relying party registration found
|
login.relyingPartyRegistrationNotFound=Ingen konfigurasjon funnet for Relying Party"
|
||||||
login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator.
|
login.userIsDisabled=Bruker er deaktivert, innlogging er for øyeblikket blokkert med dette brukernavnet. Vennligst kontakt administrator
|
||||||
login.alreadyLoggedIn=You are already logged in to
|
login.alreadyLoggedIn=Du er allerede innlogget på
|
||||||
login.alreadyLoggedIn2=devices. Please log out of the devices and try again.
|
login.alreadyLoggedIn2=enheter. Logg ut og forsøk igjen
|
||||||
login.toManySessions=You have too many active sessions
|
login.toManySessions=Du har for mange aktive økter
|
||||||
|
|
||||||
#auto-redact
|
#auto-redact
|
||||||
autoRedact.title=Automatisk Sensurering
|
autoRedact.title=Automatisk Sensurering
|
||||||
@ -591,31 +615,31 @@ autoRedact.convertPDFToImageLabel=Konverter PDF til PDF-bilde (Brukes for å fje
|
|||||||
autoRedact.submitButton=Send inn
|
autoRedact.submitButton=Send inn
|
||||||
|
|
||||||
#redact
|
#redact
|
||||||
redact.title=Manual Redaction
|
redact.title=Manuell Sensurering
|
||||||
redact.header=Manual Redaction
|
redact.header=Manuell Sensurering
|
||||||
redact.submit=Redact
|
redact.submit=Sensurer
|
||||||
redact.textBasedRedaction=Text based Redaction
|
redact.textBasedRedaction=Tekstbasert sensurering
|
||||||
redact.pageBasedRedaction=Page-based Redaction
|
redact.pageBasedRedaction=Sidebasert sensurering
|
||||||
redact.convertPDFToImageLabel=Convert PDF to PDF-Image (Used to remove text behind the box)
|
redact.convertPDFToImageLabel=Konverter PDF til PDF-bilde (Brukes for å fjerne tekst bak boksen)
|
||||||
redact.pageRedactionNumbers.title=Pages
|
redact.pageRedactionNumbers.title=Sider
|
||||||
redact.pageRedactionNumbers.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1)
|
redact.pageRedactionNumbers.placeholder=(f.eks. 1,2,8 eller 4,7,12-16 eller 2n-1)
|
||||||
redact.redactionColor.title=Redaction Color
|
redact.redactionColor.title=Sensureringsfarge
|
||||||
redact.export=Export
|
redact.export=Eksporter
|
||||||
redact.upload=Upload
|
redact.upload=Last opp
|
||||||
redact.boxRedaction=Box draw redaction
|
redact.boxRedaction=Tegn sensureringsboks
|
||||||
redact.zoom=Zoom
|
redact.zoom=Zoom
|
||||||
redact.zoomIn=Zoom in
|
redact.zoomIn=Zoom inn
|
||||||
redact.zoomOut=Zoom out
|
redact.zoomOut=Zoom ut
|
||||||
redact.nextPage=Next Page
|
redact.nextPage=Neste side
|
||||||
redact.previousPage=Previous Page
|
redact.previousPage=Forrige side
|
||||||
redact.toggleSidebar=Toggle Sidebar
|
redact.toggleSidebar=Vis/skjul sidepanel
|
||||||
redact.showThumbnails=Show Thumbnails
|
redact.showThumbnails=Vis miniatyrbilder
|
||||||
redact.showDocumentOutline=Show Document Outline (double-click to expand/collapse all items)
|
redact.showDocumentOutline=Vis dokumentstruktur (dobbeltklikk for å utvide/skjule alle elementer)
|
||||||
redact.showAttatchments=Show Attachments
|
redact.showAttatchments=Vis vedlegg
|
||||||
redact.showLayers=Show Layers (double-click to reset all layers to the default state)
|
redact.showLayers=Vis lag (dobbeltklikk for å tilbakestille alle lag til standardtilstand)
|
||||||
redact.colourPicker=Colour Picker
|
redact.colourPicker=Fargevelger
|
||||||
redact.findCurrentOutlineItem=Find current outline item
|
redact.findCurrentOutlineItem=Finn gjeldende punkt i strukturen
|
||||||
redact.applyChanges=Apply Changes
|
redact.applyChanges=Bruk endringer
|
||||||
|
|
||||||
#showJS
|
#showJS
|
||||||
showJS.title=Vis Javascript
|
showJS.title=Vis Javascript
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Rensker PDF
|
|||||||
sanitizePDF.header=Rensker en PDF fil
|
sanitizePDF.header=Rensker en PDF fil
|
||||||
sanitizePDF.selectText.1=Fjern JavaScript-handlinger
|
sanitizePDF.selectText.1=Fjern JavaScript-handlinger
|
||||||
sanitizePDF.selectText.2=Fjern innebygde filer
|
sanitizePDF.selectText.2=Fjern innebygde filer
|
||||||
sanitizePDF.selectText.3=Fjern metadata
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Fjern lenker
|
sanitizePDF.selectText.4=Fjern lenker
|
||||||
sanitizePDF.selectText.5=Fjern skrifter
|
sanitizePDF.selectText.5=Fjern skrifter
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Rensk PDF
|
sanitizePDF.submit=Rensk PDF
|
||||||
|
|
||||||
|
|
||||||
@ -836,9 +861,9 @@ compare.highlightColor.2=Uthevingsfarge 2:
|
|||||||
compare.document.1=Dokument 1
|
compare.document.1=Dokument 1
|
||||||
compare.document.2=Dokument 2
|
compare.document.2=Dokument 2
|
||||||
compare.submit=Sammenlign
|
compare.submit=Sammenlign
|
||||||
compare.complex.message=One or both of the provided documents are large files, accuracy of comparison may be reduced
|
compare.complex.message=Ett eller begge av de angitte dokumentene er store filer, nøyaktigheten av sammenligningen kan bli redusert
|
||||||
compare.large.file.message=One or Both of the provided documents are too large to process
|
compare.large.file.message=Ett eller begge av de angitte dokumentene er for store til å behandle
|
||||||
compare.no.text.message=One or both of the selected PDFs have no text content. Please choose PDFs with text for comparison.
|
compare.no.text.message=En eller begge av de valgte PDF-ene har ingen tekstinnhold. Vennligst velg PDF-er med tekst for sammenligning.
|
||||||
|
|
||||||
#sign
|
#sign
|
||||||
sign.title=Signer
|
sign.title=Signer
|
||||||
@ -848,20 +873,20 @@ sign.draw=Tegn signatur
|
|||||||
sign.text=Tekstinput
|
sign.text=Tekstinput
|
||||||
sign.clear=Slett
|
sign.clear=Slett
|
||||||
sign.add=Legg til
|
sign.add=Legg til
|
||||||
sign.saved=Saved Signatures
|
sign.saved=Lagrede signaturer
|
||||||
sign.save=Save Signature
|
sign.save=Lagre signatur
|
||||||
sign.personalSigs=Personal Signatures
|
sign.personalSigs=Personlige signaturer
|
||||||
sign.sharedSigs=Shared Signatures
|
sign.sharedSigs=Delte signaturer
|
||||||
sign.noSavedSigs=No saved signatures found
|
sign.noSavedSigs=Ingen lagrede signaturer funnet
|
||||||
sign.addToAll=Add to all pages
|
sign.addToAll=Legg til på alle sider
|
||||||
sign.delete=Delete
|
sign.delete=Slett
|
||||||
sign.first=First page
|
sign.first=Første side
|
||||||
sign.last=Last page
|
sign.last=Siste side
|
||||||
sign.next=Next page
|
sign.next=Neste side
|
||||||
sign.previous=Previous page
|
sign.previous=Forrige side
|
||||||
sign.maintainRatio=Toggle maintain aspect ratio
|
sign.maintainRatio=Bytt behold sideforhold
|
||||||
sign.undo=Undo
|
sign.undo=Angre
|
||||||
sign.redo=Redo
|
sign.redo=Gjør om
|
||||||
|
|
||||||
#repair
|
#repair
|
||||||
repair.title=Reparer
|
repair.title=Reparer
|
||||||
@ -887,7 +912,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.
|
ScannerImageSplit.info=Python er ikke installert. Det er påkrevd for å kjøre.
|
||||||
|
|
||||||
|
|
||||||
#OCR
|
#OCR
|
||||||
@ -1333,43 +1358,43 @@ fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here
|
|||||||
fileChooser.extractPDF=Extracting...
|
fileChooser.extractPDF=Extracting...
|
||||||
|
|
||||||
#release notes
|
#release notes
|
||||||
releases.footer=Releases
|
releases.footer=Versjoner
|
||||||
releases.title=Release Notes
|
releases.title=Versjonsnotater
|
||||||
releases.header=Release Notes
|
releases.header=Versjonsnotater
|
||||||
releases.current.version=Current Release
|
releases.current.version=Gjeldende Versjon
|
||||||
releases.note=Release notes are only available in English
|
releases.note=Versjonsnotater er kun tilgjengelige på engelsk
|
||||||
|
|
||||||
#Validate Signature
|
#Validate Signature
|
||||||
validateSignature.title=Validate PDF Signatures
|
validateSignature.title=Valider PDF-signaturer
|
||||||
validateSignature.header=Validate Digital Signatures
|
validateSignature.header=Valider Digitale Signaturer
|
||||||
validateSignature.selectPDF=Select signed PDF file
|
validateSignature.selectPDF=Velg signert PDF-fil
|
||||||
validateSignature.submit=Validate Signatures
|
validateSignature.submit=Valider Signaturer
|
||||||
validateSignature.results=Validation Results
|
validateSignature.results=Valideringsresultater
|
||||||
validateSignature.status=Status
|
validateSignature.status=Status
|
||||||
validateSignature.signer=Signer
|
validateSignature.signer=Signatar
|
||||||
validateSignature.date=Date
|
validateSignature.date=Dato
|
||||||
validateSignature.reason=Reason
|
validateSignature.reason=Årsak
|
||||||
validateSignature.location=Location
|
validateSignature.location=Sted
|
||||||
validateSignature.noSignatures=No digital signatures found in this document
|
validateSignature.noSignatures=Ingen digitale signaturer funnet i dette dokumentet
|
||||||
validateSignature.status.valid=Valid
|
validateSignature.status.valid=Gyldig
|
||||||
validateSignature.status.invalid=Invalid
|
validateSignature.status.invalid=Ugyldig
|
||||||
validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity
|
validateSignature.chain.invalid=Validering av sertifikatkjede feilet - kan ikke verifisere signatarens identitet
|
||||||
validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified
|
validateSignature.trust.invalid=Sertifikatet er ikke i tillitslager - kilden kan ikke verifiseres
|
||||||
validateSignature.cert.expired=Certificate has expired
|
validateSignature.cert.expired=Sertifikatet har utløpt
|
||||||
validateSignature.cert.revoked=Certificate has been revoked
|
validateSignature.cert.revoked=Sertifikatet har blitt tilbakekalt
|
||||||
validateSignature.signature.info=Signature Information
|
validateSignature.signature.info=Signaturinformasjon
|
||||||
validateSignature.signature=Signature
|
validateSignature.signature=Signatur
|
||||||
validateSignature.signature.mathValid=Signature is mathematically valid BUT:
|
validateSignature.signature.mathValid=Signaturen er matematisk gyldig MEN:
|
||||||
validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional)
|
validateSignature.selectCustomCert=Tilpasset Sertifikatfil X.509 (Valgfritt)
|
||||||
validateSignature.cert.info=Certificate Details
|
validateSignature.cert.info=Sertifikatdetaljer
|
||||||
validateSignature.cert.issuer=Issuer
|
validateSignature.cert.issuer=Utsteder
|
||||||
validateSignature.cert.subject=Subject
|
validateSignature.cert.subject=Emne
|
||||||
validateSignature.cert.serialNumber=Serial Number
|
validateSignature.cert.serialNumber=Serienummer
|
||||||
validateSignature.cert.validFrom=Valid From
|
validateSignature.cert.validFrom=Gyldig Fra
|
||||||
validateSignature.cert.validUntil=Valid Until
|
validateSignature.cert.validUntil=Gyldig Til
|
||||||
validateSignature.cert.algorithm=Algorithm
|
validateSignature.cert.algorithm=Algoritme
|
||||||
validateSignature.cert.keySize=Key Size
|
validateSignature.cert.keySize=Nøkkelstørrelse
|
||||||
validateSignature.cert.version=Version
|
validateSignature.cert.version=Versjon
|
||||||
validateSignature.cert.keyUsage=Key Usage
|
validateSignature.cert.keyUsage=Nøkkelbruk
|
||||||
validateSignature.cert.selfSigned=Self-Signed
|
validateSignature.cert.selfSigned=Selv-signert
|
||||||
validateSignature.cert.bits=bits
|
validateSignature.cert.bits=bits
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Aktywni Użytkownicy:
|
|||||||
adminUserSettings.disabledUsers=Wyłączeni Użytkownicy:
|
adminUserSettings.disabledUsers=Wyłączeni Użytkownicy:
|
||||||
adminUserSettings.totalUsers=Łączna Liczba Użytkowników:
|
adminUserSettings.totalUsers=Łączna Liczba Użytkowników:
|
||||||
adminUserSettings.lastRequest=Ostatnie Zgłoszenie
|
adminUserSettings.lastRequest=Ostatnie Zgłoszenie
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Import/Eksport bazy danych
|
database.title=Import/Eksport bazy danych
|
||||||
database.header=Import/Eksport bazy danych
|
database.header=Import/Eksport bazy danych
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Dezynfekuj PDF
|
|||||||
sanitizePDF.header=Dezynfekuj dokument PDF
|
sanitizePDF.header=Dezynfekuj dokument PDF
|
||||||
sanitizePDF.selectText.1=Usuń elementy JavaScript
|
sanitizePDF.selectText.1=Usuń elementy JavaScript
|
||||||
sanitizePDF.selectText.2=Usuń załączone pliki
|
sanitizePDF.selectText.2=Usuń załączone pliki
|
||||||
sanitizePDF.selectText.3=Usuń metadane
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Usuń linki
|
sanitizePDF.selectText.4=Usuń linki
|
||||||
sanitizePDF.selectText.5=Usuń czcionki
|
sanitizePDF.selectText.5=Usuń czcionki
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Dezynfekuj PDF
|
sanitizePDF.submit=Dezynfekuj PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Usuários Ativos:
|
|||||||
adminUserSettings.disabledUsers=Usuários Desabilitados:
|
adminUserSettings.disabledUsers=Usuários Desabilitados:
|
||||||
adminUserSettings.totalUsers=Total de Usuários:
|
adminUserSettings.totalUsers=Total de Usuários:
|
||||||
adminUserSettings.lastRequest=Última solicitação
|
adminUserSettings.lastRequest=Última solicitação
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Importar/Exportar banco de dados
|
database.title=Importar/Exportar banco de dados
|
||||||
database.header=Importar/Exportar banco de dados
|
database.header=Importar/Exportar banco de dados
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Higienizar
|
|||||||
sanitizePDF.header=Higienizar
|
sanitizePDF.header=Higienizar
|
||||||
sanitizePDF.selectText.1=Remover scripts de JavaScript.
|
sanitizePDF.selectText.1=Remover scripts de JavaScript.
|
||||||
sanitizePDF.selectText.2=Remover arquivos embutidos.
|
sanitizePDF.selectText.2=Remover arquivos embutidos.
|
||||||
sanitizePDF.selectText.3=Remover metadados.
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Remover links.
|
sanitizePDF.selectText.4=Remover links.
|
||||||
sanitizePDF.selectText.5=Remover fontes.
|
sanitizePDF.selectText.5=Remover fontes.
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Higienizar PDF
|
sanitizePDF.submit=Higienizar PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Utilizadores Ativos:
|
|||||||
adminUserSettings.disabledUsers=Utilizadores Desativados:
|
adminUserSettings.disabledUsers=Utilizadores Desativados:
|
||||||
adminUserSettings.totalUsers=Total de Utilizadores:
|
adminUserSettings.totalUsers=Total de Utilizadores:
|
||||||
adminUserSettings.lastRequest=Último Pedido
|
adminUserSettings.lastRequest=Último Pedido
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Importar/Exportar Base de Dados
|
database.title=Importar/Exportar Base de Dados
|
||||||
database.header=Importar/Exportar Base de Dados
|
database.header=Importar/Exportar Base de Dados
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Sanitizar PDF
|
|||||||
sanitizePDF.header=Sanitizar um ficheiro PDF
|
sanitizePDF.header=Sanitizar um ficheiro PDF
|
||||||
sanitizePDF.selectText.1=Remover ações JavaScript
|
sanitizePDF.selectText.1=Remover ações JavaScript
|
||||||
sanitizePDF.selectText.2=Remover ficheiros incorporados
|
sanitizePDF.selectText.2=Remover ficheiros incorporados
|
||||||
sanitizePDF.selectText.3=Remover metadados
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Remover ligações
|
sanitizePDF.selectText.4=Remover ligações
|
||||||
sanitizePDF.selectText.5=Remover tipos de letra
|
sanitizePDF.selectText.5=Remover tipos de letra
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Sanitizar PDF
|
sanitizePDF.submit=Sanitizar PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Utilizatori Activi:
|
|||||||
adminUserSettings.disabledUsers=Utilizatori Dezactivați:
|
adminUserSettings.disabledUsers=Utilizatori Dezactivați:
|
||||||
adminUserSettings.totalUsers=Total Utilizatori:
|
adminUserSettings.totalUsers=Total Utilizatori:
|
||||||
adminUserSettings.lastRequest=Ultima Cerere
|
adminUserSettings.lastRequest=Ultima Cerere
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Import/Export Bază de Date
|
database.title=Import/Export Bază de Date
|
||||||
database.header=Import/Export Bază de Date
|
database.header=Import/Export Bază de Date
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Igienizează PDF
|
|||||||
sanitizePDF.header=Igienizează un fișier PDF
|
sanitizePDF.header=Igienizează un fișier PDF
|
||||||
sanitizePDF.selectText.1=Elimină acțiunile JavaScript
|
sanitizePDF.selectText.1=Elimină acțiunile JavaScript
|
||||||
sanitizePDF.selectText.2=Elimină fișierele încorporate
|
sanitizePDF.selectText.2=Elimină fișierele încorporate
|
||||||
sanitizePDF.selectText.3=Elimină metadatele
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Elimină link-urile
|
sanitizePDF.selectText.4=Elimină link-urile
|
||||||
sanitizePDF.selectText.5=Elimină fonturile
|
sanitizePDF.selectText.5=Elimină fonturile
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Igienizează PDF
|
sanitizePDF.submit=Igienizează PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Активные пользователи:
|
|||||||
adminUserSettings.disabledUsers=Отключенные пользователи:
|
adminUserSettings.disabledUsers=Отключенные пользователи:
|
||||||
adminUserSettings.totalUsers=Всего пользователей:
|
adminUserSettings.totalUsers=Всего пользователей:
|
||||||
adminUserSettings.lastRequest=Последний запрос
|
adminUserSettings.lastRequest=Последний запрос
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Импорт/экспорт базы данных
|
database.title=Импорт/экспорт базы данных
|
||||||
database.header=Импорт/экспорт базы данных
|
database.header=Импорт/экспорт базы данных
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Очистить PDF
|
|||||||
sanitizePDF.header=Очистить PDF-файл
|
sanitizePDF.header=Очистить PDF-файл
|
||||||
sanitizePDF.selectText.1=Удалить JavaScript-действия
|
sanitizePDF.selectText.1=Удалить JavaScript-действия
|
||||||
sanitizePDF.selectText.2=Удалить встроенные файлы
|
sanitizePDF.selectText.2=Удалить встроенные файлы
|
||||||
sanitizePDF.selectText.3=Удалить метаданные
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Удалить ссылки
|
sanitizePDF.selectText.4=Удалить ссылки
|
||||||
sanitizePDF.selectText.5=Удалить шрифты
|
sanitizePDF.selectText.5=Удалить шрифты
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Очистить PDF
|
sanitizePDF.submit=Очистить PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Active Users:
|
|||||||
adminUserSettings.disabledUsers=Disabled Users:
|
adminUserSettings.disabledUsers=Disabled Users:
|
||||||
adminUserSettings.totalUsers=Total Users:
|
adminUserSettings.totalUsers=Total Users:
|
||||||
adminUserSettings.lastRequest=Last Request
|
adminUserSettings.lastRequest=Last Request
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Database Import/Export
|
database.title=Database Import/Export
|
||||||
database.header=Database Import/Export
|
database.header=Database Import/Export
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Vyčistiť PDF
|
|||||||
sanitizePDF.header=Vyčistiť PDF súbor
|
sanitizePDF.header=Vyčistiť PDF súbor
|
||||||
sanitizePDF.selectText.1=Odstrániť JavaScript akcie
|
sanitizePDF.selectText.1=Odstrániť JavaScript akcie
|
||||||
sanitizePDF.selectText.2=Odstrániť vložené súbory
|
sanitizePDF.selectText.2=Odstrániť vložené súbory
|
||||||
sanitizePDF.selectText.3=Odstrániť metadáta
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Odstrániť odkazy
|
sanitizePDF.selectText.4=Odstrániť odkazy
|
||||||
sanitizePDF.selectText.5=Odstrániť fonty
|
sanitizePDF.selectText.5=Odstrániť fonty
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Vyčistiť PDF
|
sanitizePDF.submit=Vyčistiť PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Aktivni uporabniki:
|
|||||||
adminUserSettings.disabledUsers=Onemogočeni uporabniki:
|
adminUserSettings.disabledUsers=Onemogočeni uporabniki:
|
||||||
adminUserSettings.totalUsers=Skupno število uporabnikov:
|
adminUserSettings.totalUsers=Skupno število uporabnikov:
|
||||||
adminUserSettings.lastRequest=Zadnja zahteva
|
adminUserSettings.lastRequest=Zadnja zahteva
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Uvoz/izvoz baze podatkov
|
database.title=Uvoz/izvoz baze podatkov
|
||||||
database.header=Uvoz/izvoz baze podatkov
|
database.header=Uvoz/izvoz baze podatkov
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Prečisti PDF
|
|||||||
sanitizePDF.header=Prečisti datoteko PDF
|
sanitizePDF.header=Prečisti datoteko PDF
|
||||||
sanitizePDF.selectText.1=Odstrani dejanja JavaScript
|
sanitizePDF.selectText.1=Odstrani dejanja JavaScript
|
||||||
sanitizePDF.selectText.2=Odstrani vdelane datoteke
|
sanitizePDF.selectText.2=Odstrani vdelane datoteke
|
||||||
sanitizePDF.selectText.3=Odstrani metapodatke
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Odstrani povezave
|
sanitizePDF.selectText.4=Odstrani povezave
|
||||||
sanitizePDF.selectText.5=Odstrani pisave
|
sanitizePDF.selectText.5=Odstrani pisave
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Prečisti PDF
|
sanitizePDF.submit=Prečisti PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Active Users:
|
|||||||
adminUserSettings.disabledUsers=Disabled Users:
|
adminUserSettings.disabledUsers=Disabled Users:
|
||||||
adminUserSettings.totalUsers=Total Users:
|
adminUserSettings.totalUsers=Total Users:
|
||||||
adminUserSettings.lastRequest=Last Request
|
adminUserSettings.lastRequest=Last Request
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Database Import/Export
|
database.title=Database Import/Export
|
||||||
database.header=Database Import/Export
|
database.header=Database Import/Export
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Sanitizacija PDF-a
|
|||||||
sanitizePDF.header=Sanitizacija PDF fajla
|
sanitizePDF.header=Sanitizacija PDF fajla
|
||||||
sanitizePDF.selectText.1=Ukloni JavaScript akcije
|
sanitizePDF.selectText.1=Ukloni JavaScript akcije
|
||||||
sanitizePDF.selectText.2=Ukloni ugrađene fajlove
|
sanitizePDF.selectText.2=Ukloni ugrađene fajlove
|
||||||
sanitizePDF.selectText.3=Ukloni metapodatke
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Ukloni linkove
|
sanitizePDF.selectText.4=Ukloni linkove
|
||||||
sanitizePDF.selectText.5=Ukloni fontove
|
sanitizePDF.selectText.5=Ukloni fontove
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Sanitizuj PDF
|
sanitizePDF.submit=Sanitizuj PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Aktiva användare:
|
|||||||
adminUserSettings.disabledUsers=Inaktiverade användare:
|
adminUserSettings.disabledUsers=Inaktiverade användare:
|
||||||
adminUserSettings.totalUsers=Totalt antal användare:
|
adminUserSettings.totalUsers=Totalt antal användare:
|
||||||
adminUserSettings.lastRequest=Senaste begäran
|
adminUserSettings.lastRequest=Senaste begäran
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Databasimport/export
|
database.title=Databasimport/export
|
||||||
database.header=Databasimport/export
|
database.header=Databasimport/export
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Sanera PDF
|
|||||||
sanitizePDF.header=Sanera en PDF-fil
|
sanitizePDF.header=Sanera en PDF-fil
|
||||||
sanitizePDF.selectText.1=Ta bort JavaScript-åtgärder
|
sanitizePDF.selectText.1=Ta bort JavaScript-åtgärder
|
||||||
sanitizePDF.selectText.2=Ta bort inbäddade filer
|
sanitizePDF.selectText.2=Ta bort inbäddade filer
|
||||||
sanitizePDF.selectText.3=Ta bort metadata
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Ta bort länkar
|
sanitizePDF.selectText.4=Ta bort länkar
|
||||||
sanitizePDF.selectText.5=Ta bort typsnitt
|
sanitizePDF.selectText.5=Ta bort typsnitt
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Sanera PDF
|
sanitizePDF.submit=Sanera PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=ผู้ใช้ที่มีการใช
|
|||||||
adminUserSettings.disabledUsers=ผู้ใช้ที่ถูกระงับการใช้งาน:
|
adminUserSettings.disabledUsers=ผู้ใช้ที่ถูกระงับการใช้งาน:
|
||||||
adminUserSettings.totalUsers=ผู้ใช้รวมทั้งหมด:
|
adminUserSettings.totalUsers=ผู้ใช้รวมทั้งหมด:
|
||||||
adminUserSettings.lastRequest=การขอข้อมูลล่าสุด
|
adminUserSettings.lastRequest=การขอข้อมูลล่าสุด
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=การนำเข้า/ส่งออกฐานข้อมูล
|
database.title=การนำเข้า/ส่งออกฐานข้อมูล
|
||||||
database.header=การนำเข้า/ส่งออกฐานข้อมูล
|
database.header=การนำเข้า/ส่งออกฐานข้อมูล
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=ทำความสะอาด PDF
|
|||||||
sanitizePDF.header=ทำความสะอาดไฟล์ PDF
|
sanitizePDF.header=ทำความสะอาดไฟล์ PDF
|
||||||
sanitizePDF.selectText.1=ลบการกระทำ JavaScript
|
sanitizePDF.selectText.1=ลบการกระทำ JavaScript
|
||||||
sanitizePDF.selectText.2=ลบไฟล์ฝังตัว
|
sanitizePDF.selectText.2=ลบไฟล์ฝังตัว
|
||||||
sanitizePDF.selectText.3=ลบข้อมูลเมตา
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=ลบลิงก์
|
sanitizePDF.selectText.4=ลบลิงก์
|
||||||
sanitizePDF.selectText.5=ลบฟอนต์
|
sanitizePDF.selectText.5=ลบฟอนต์
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=ทำความสะอาด PDF
|
sanitizePDF.submit=ทำความสะอาด PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Aktif Kullanıcılar:
|
|||||||
adminUserSettings.disabledUsers=Devre Dışı Kullanıcılar:
|
adminUserSettings.disabledUsers=Devre Dışı Kullanıcılar:
|
||||||
adminUserSettings.totalUsers=Toplam Kullanıcılar:
|
adminUserSettings.totalUsers=Toplam Kullanıcılar:
|
||||||
adminUserSettings.lastRequest=Son İstek
|
adminUserSettings.lastRequest=Son İstek
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Veri Tabanını İçe/Dışa Aktar
|
database.title=Veri Tabanını İçe/Dışa Aktar
|
||||||
database.header=Veri Tabanını İçe/Dışa Aktar
|
database.header=Veri Tabanını İçe/Dışa Aktar
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=PDF'i Temizle
|
|||||||
sanitizePDF.header=PDF dosyasını temizle
|
sanitizePDF.header=PDF dosyasını temizle
|
||||||
sanitizePDF.selectText.1=JavaScript işlemlerini kaldır
|
sanitizePDF.selectText.1=JavaScript işlemlerini kaldır
|
||||||
sanitizePDF.selectText.2=Gömülü dosyaları kaldır
|
sanitizePDF.selectText.2=Gömülü dosyaları kaldır
|
||||||
sanitizePDF.selectText.3=Üst veriyi kaldır
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Linkleri kaldır
|
sanitizePDF.selectText.4=Linkleri kaldır
|
||||||
sanitizePDF.selectText.5=Fontları kaldır
|
sanitizePDF.selectText.5=Fontları kaldır
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=PDF'i Temizle
|
sanitizePDF.submit=PDF'i Temizle
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Активні користувачі:
|
|||||||
adminUserSettings.disabledUsers=Заблоковані користувачі:
|
adminUserSettings.disabledUsers=Заблоковані користувачі:
|
||||||
adminUserSettings.totalUsers=Всього користувачів:
|
adminUserSettings.totalUsers=Всього користувачів:
|
||||||
adminUserSettings.lastRequest=Останній запит
|
adminUserSettings.lastRequest=Останній запит
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Імпорт/експорт бази даних
|
database.title=Імпорт/експорт бази даних
|
||||||
database.header=Імпорт/експорт бази даних
|
database.header=Імпорт/експорт бази даних
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Дезінфекція PDF
|
|||||||
sanitizePDF.header=Дезінфекція PDF файлу
|
sanitizePDF.header=Дезінфекція PDF файлу
|
||||||
sanitizePDF.selectText.1=Видалити JavaScript
|
sanitizePDF.selectText.1=Видалити JavaScript
|
||||||
sanitizePDF.selectText.2=Видалити вбудовані файли
|
sanitizePDF.selectText.2=Видалити вбудовані файли
|
||||||
sanitizePDF.selectText.3=Видалити метадані
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Видалити посилання
|
sanitizePDF.selectText.4=Видалити посилання
|
||||||
sanitizePDF.selectText.5=Видалити шрифти
|
sanitizePDF.selectText.5=Видалити шрифти
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Дезінфекція
|
sanitizePDF.submit=Дезінфекція
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=Active Users:
|
|||||||
adminUserSettings.disabledUsers=Disabled Users:
|
adminUserSettings.disabledUsers=Disabled Users:
|
||||||
adminUserSettings.totalUsers=Total Users:
|
adminUserSettings.totalUsers=Total Users:
|
||||||
adminUserSettings.lastRequest=Last Request
|
adminUserSettings.lastRequest=Last Request
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=Nhập/Xuất cơ sở dữ liệu
|
database.title=Nhập/Xuất cơ sở dữ liệu
|
||||||
database.header=Nhập/Xuất cơ sở dữ liệu
|
database.header=Nhập/Xuất cơ sở dữ liệu
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=Làm sạch PDF
|
|||||||
sanitizePDF.header=Làm sạch tệp PDF
|
sanitizePDF.header=Làm sạch tệp PDF
|
||||||
sanitizePDF.selectText.1=Xóa các hành động JavaScript
|
sanitizePDF.selectText.1=Xóa các hành động JavaScript
|
||||||
sanitizePDF.selectText.2=Xóa các tệp nhúng
|
sanitizePDF.selectText.2=Xóa các tệp nhúng
|
||||||
sanitizePDF.selectText.3=Xóa metadata
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=Xóa liên kết
|
sanitizePDF.selectText.4=Xóa liên kết
|
||||||
sanitizePDF.selectText.5=Xóa phông chữ
|
sanitizePDF.selectText.5=Xóa phông chữ
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=Làm sạch PDF
|
sanitizePDF.submit=Làm sạch PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=འགུལ་བཞིན་པའི་སྤ
|
|||||||
adminUserSettings.disabledUsers=སྤྱོད་མི་ཆོག་པའི་སྤྱོད་མཁན།
|
adminUserSettings.disabledUsers=སྤྱོད་མི་ཆོག་པའི་སྤྱོད་མཁན།
|
||||||
adminUserSettings.totalUsers=སྤྱོད་མཁན་ཁྱོན་བསྡོམས།
|
adminUserSettings.totalUsers=སྤྱོད་མཁན་ཁྱོན་བསྡོམས།
|
||||||
adminUserSettings.lastRequest=རེ་ཞུ་མཐའ་མ།
|
adminUserSettings.lastRequest=རེ་ཞུ་མཐའ་མ།
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=གཞི་གྲངས་མཛོད་ནང་འདྲེན་/ཕྱིར་འདྲེན།
|
database.title=གཞི་གྲངས་མཛོད་ནང་འདྲེན་/ཕྱིར་འདྲེན།
|
||||||
database.header=གཞི་གྲངས་མཛོད་ནང་འདྲེན་/ཕྱིར་འདྲེན།
|
database.header=གཞི་གྲངས་མཛོད་ནང་འདྲེན་/ཕྱིར་འདྲེན།
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=PDF གཙང་སེལ།
|
|||||||
sanitizePDF.header=PDF ཡིག་ཆ་གཙང་སེལ།
|
sanitizePDF.header=PDF ཡིག་ཆ་གཙང་སེལ།
|
||||||
sanitizePDF.selectText.1=Javascript བྱ་འགུལ་སུབ་པ།
|
sanitizePDF.selectText.1=Javascript བྱ་འགུལ་སུབ་པ།
|
||||||
sanitizePDF.selectText.2=ནང་འཇུག་ཡིག་ཆ་སུབ་པ།
|
sanitizePDF.selectText.2=ནང་འཇུག་ཡིག་ཆ་སུབ་པ།
|
||||||
sanitizePDF.selectText.3=གནས་ཆ་སུབ་པ།
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=འབྲེལ་ཐག་སུབ་པ།
|
sanitizePDF.selectText.4=འབྲེལ་ཐག་སུབ་པ།
|
||||||
sanitizePDF.selectText.5=ཡིག་གཟུགས་སུབ་པ།
|
sanitizePDF.selectText.5=ཡིག་གཟུགས་སུབ་པ།
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=PDF གཙང་སེལ།
|
sanitizePDF.submit=PDF གཙང་སེལ།
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=激活用户:
|
|||||||
adminUserSettings.disabledUsers=禁用用户:
|
adminUserSettings.disabledUsers=禁用用户:
|
||||||
adminUserSettings.totalUsers=总用户:
|
adminUserSettings.totalUsers=总用户:
|
||||||
adminUserSettings.lastRequest=最后登录
|
adminUserSettings.lastRequest=最后登录
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=数据库 导入/导出
|
database.title=数据库 导入/导出
|
||||||
database.header=数据库 导入/导出
|
database.header=数据库 导入/导出
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=清理 PDF
|
|||||||
sanitizePDF.header=清理 PDF 文件
|
sanitizePDF.header=清理 PDF 文件
|
||||||
sanitizePDF.selectText.1=移除 JavaScript 操作
|
sanitizePDF.selectText.1=移除 JavaScript 操作
|
||||||
sanitizePDF.selectText.2=移除嵌入的文件
|
sanitizePDF.selectText.2=移除嵌入的文件
|
||||||
sanitizePDF.selectText.3=移除元数据
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=移除链接
|
sanitizePDF.selectText.4=移除链接
|
||||||
sanitizePDF.selectText.5=移除字体
|
sanitizePDF.selectText.5=移除字体
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=清理PDF
|
sanitizePDF.submit=清理PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,31 @@ adminUserSettings.activeUsers=使用中的使用者:
|
|||||||
adminUserSettings.disabledUsers=已停用的使用者:
|
adminUserSettings.disabledUsers=已停用的使用者:
|
||||||
adminUserSettings.totalUsers=使用者總數:
|
adminUserSettings.totalUsers=使用者總數:
|
||||||
adminUserSettings.lastRequest=最後請求時間
|
adminUserSettings.lastRequest=最後請求時間
|
||||||
|
adminUserSettings.usage=View Usage
|
||||||
|
|
||||||
|
endpointStatistics.title=Endpoint Statistics
|
||||||
|
endpointStatistics.header=Endpoint Statistics
|
||||||
|
endpointStatistics.top10=Top 10
|
||||||
|
endpointStatistics.top20=Top 20
|
||||||
|
endpointStatistics.all=All
|
||||||
|
endpointStatistics.refresh=Refresh
|
||||||
|
endpointStatistics.includeHomepage=Include Homepage ('/')
|
||||||
|
endpointStatistics.includeLoginPage=Include Login Page ('/login')
|
||||||
|
endpointStatistics.totalEndpoints=Total Endpoints
|
||||||
|
endpointStatistics.totalVisits=Total Visits
|
||||||
|
endpointStatistics.showing=Showing
|
||||||
|
endpointStatistics.selectedVisits=Selected Visits
|
||||||
|
endpointStatistics.endpoint=Endpoint
|
||||||
|
endpointStatistics.visits=Visits
|
||||||
|
endpointStatistics.percentage=Percentage
|
||||||
|
endpointStatistics.loading=Loading...
|
||||||
|
endpointStatistics.failedToLoad=Failed to load endpoint data. Please try refreshing.
|
||||||
|
endpointStatistics.home=Home
|
||||||
|
endpointStatistics.login=Login
|
||||||
|
endpointStatistics.top=Top
|
||||||
|
endpointStatistics.numberOfVisits=Number of Visits
|
||||||
|
endpointStatistics.visitsTooltip=Visits: {0} ({1}% of total)
|
||||||
|
endpointStatistics.retry=Retry
|
||||||
|
|
||||||
database.title=資料庫匯入/匯出
|
database.title=資料庫匯入/匯出
|
||||||
database.header=資料庫匯入/匯出
|
database.header=資料庫匯入/匯出
|
||||||
@ -709,9 +733,10 @@ sanitizePDF.title=清理 PDF
|
|||||||
sanitizePDF.header=清理 PDF 檔案
|
sanitizePDF.header=清理 PDF 檔案
|
||||||
sanitizePDF.selectText.1=移除 JavaScript 操作
|
sanitizePDF.selectText.1=移除 JavaScript 操作
|
||||||
sanitizePDF.selectText.2=移除內嵌文件
|
sanitizePDF.selectText.2=移除內嵌文件
|
||||||
sanitizePDF.selectText.3=移除中繼資料
|
sanitizePDF.selectText.3=Remove XMP metadata
|
||||||
sanitizePDF.selectText.4=移除連結
|
sanitizePDF.selectText.4=移除連結
|
||||||
sanitizePDF.selectText.5=移除字型
|
sanitizePDF.selectText.5=移除字型
|
||||||
|
sanitizePDF.selectText.6=Remove Document Info Metadata
|
||||||
sanitizePDF.submit=清理 PDF
|
sanitizePDF.submit=清理 PDF
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,15 +61,17 @@ security:
|
|||||||
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
|
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
|
||||||
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
|
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
|
||||||
|
|
||||||
enterpriseEdition:
|
|
||||||
enabled: false # set to 'true' to enable enterprise edition
|
premium:
|
||||||
key: 00000000-0000-0000-0000-000000000000
|
key: 00000000-0000-0000-0000-000000000000
|
||||||
SSOAutoLogin: false # Enable to auto login to first provided SSO
|
enabled: false # Enable license key checks for pro/enterprise features
|
||||||
CustomMetadata:
|
proFeatures:
|
||||||
autoUpdateMetadata: false # set to 'true' to automatically update metadata with below values
|
SSOAutoLogin: false
|
||||||
author: username # supports text such as 'John Doe' or types such as username to autopopulate with user's username
|
CustomMetadata:
|
||||||
creator: Stirling-PDF # supports text such as 'Company-PDF'
|
autoUpdateMetadata: false
|
||||||
producer: Stirling-PDF # supports text such as 'Company-PDF'
|
author: username
|
||||||
|
creator: Stirling-PDF
|
||||||
|
producer: Stirling-PDF
|
||||||
|
|
||||||
legal:
|
legal:
|
||||||
termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
|
termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
|
||||||
@ -87,6 +89,7 @@ system:
|
|||||||
customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template HTML files
|
customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template HTML files
|
||||||
tessdataDir: /usr/share/tessdata # path to the directory containing the Tessdata files. This setting is relevant for Windows systems. For Windows users, this path should be adjusted to point to the appropriate directory where the Tessdata files are stored.
|
tessdataDir: /usr/share/tessdata # path to the directory containing the Tessdata files. This setting is relevant for Windows systems. For Windows users, this path should be adjusted to point to the appropriate directory where the Tessdata files are stored.
|
||||||
enableAnalytics: null # set to 'true' to enable analytics, set to 'false' to disable analytics; for enterprise users, this is set to true
|
enableAnalytics: null # set to 'true' to enable analytics, set to 'false' to disable analytics; for enterprise users, this is set to true
|
||||||
|
enableUrlToPDF: false # Set to 'true' to enable URL to PDF, INTERNAL ONLY, known security issues, should not be used externally
|
||||||
disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML)
|
disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML)
|
||||||
datasource:
|
datasource:
|
||||||
enableCustomDatabase: false # Enterprise users ONLY, set this property to 'true' if you would like to use your own custom database configuration
|
enableCustomDatabase: false # Enterprise users ONLY, set this property to 'true' if you would like to use your own custom database configuration
|
||||||
|
@ -571,6 +571,49 @@
|
|||||||
"moduleLicense": "The Apache Software License, Version 2.0",
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"moduleName": "io.micrometer:micrometer-registry-prometheus",
|
||||||
|
"moduleUrl": "https://github.com/micrometer-metrics/micrometer",
|
||||||
|
"moduleVersion": "1.14.5",
|
||||||
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
|
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleName": "io.prometheus:prometheus-metrics-config",
|
||||||
|
"moduleVersion": "1.3.6",
|
||||||
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
|
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleName": "io.prometheus:prometheus-metrics-core",
|
||||||
|
"moduleVersion": "1.3.6",
|
||||||
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
|
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleName": "io.prometheus:prometheus-metrics-exposition-formats",
|
||||||
|
"moduleVersion": "1.3.6",
|
||||||
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
|
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleName": "io.prometheus:prometheus-metrics-exposition-textformats",
|
||||||
|
"moduleVersion": "1.3.6",
|
||||||
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
|
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleName": "io.prometheus:prometheus-metrics-model",
|
||||||
|
"moduleVersion": "1.3.6",
|
||||||
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
|
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"moduleName": "io.prometheus:prometheus-metrics-tracer-common",
|
||||||
|
"moduleVersion": "1.3.6",
|
||||||
|
"moduleLicense": "The Apache Software License, Version 2.0",
|
||||||
|
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"moduleName": "io.smallrye:jandex",
|
"moduleName": "io.smallrye:jandex",
|
||||||
"moduleVersion": "3.2.0",
|
"moduleVersion": "3.2.0",
|
||||||
|
83
src/main/resources/static/css/usage.css
Normal file
83
src/main/resources/static/css/usage.css
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
.active-user {
|
||||||
|
color: green;
|
||||||
|
text-shadow: 0 0 5px green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-overflow {
|
||||||
|
max-width: 100px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
height: 400px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-box {
|
||||||
|
background: var(--md-sys-color-outline-variant);
|
||||||
|
padding: .8rem;
|
||||||
|
margin: 10px 0;
|
||||||
|
border-radius: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 15px;
|
||||||
|
margin-top: 15px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-checkbox {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-checkbox input {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-bs-theme="light"] .loading {
|
||||||
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-bs-theme="dark"] .loading {
|
||||||
|
background-color: rgba(15, 20, 26, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add some animation to the refresh button */
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.refreshing .material-symbols-rounded {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
20
src/main/resources/static/js/thirdParty/chart.umd.min.js
vendored
Normal file
20
src/main/resources/static/js/thirdParty/chart.umd.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
363
src/main/resources/static/js/usage.js
Normal file
363
src/main/resources/static/js/usage.js
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
// We'll fetch data from the API instead of hardcoding it
|
||||||
|
let allEndpointData = [];
|
||||||
|
let filteredData = [];
|
||||||
|
|
||||||
|
// We'll store these as global variables that get updated when we fetch data
|
||||||
|
let sortedData = [];
|
||||||
|
let totalEndpoints = 0;
|
||||||
|
let totalVisits = 0;
|
||||||
|
|
||||||
|
// Chart instance
|
||||||
|
let myChart;
|
||||||
|
|
||||||
|
|
||||||
|
// Function to get chart colors based on current theme
|
||||||
|
function getChartColors() {
|
||||||
|
var style = window.getComputedStyle(document.body)
|
||||||
|
|
||||||
|
const colours = {
|
||||||
|
textColor: style.getPropertyValue('--md-sys-color-on-surface') ,
|
||||||
|
primaryColor: style.getPropertyValue('--md-sys-color-primary'),
|
||||||
|
backgroundColor: style.getPropertyValue('--md-sys-color-background'),
|
||||||
|
gridColor: style.getPropertyValue('--md-sys-color-on-surface'),
|
||||||
|
tooltipBgColor: style.getPropertyValue('--md-sys-color-inverse-on-surface'),
|
||||||
|
tooltipTextColor: style.getPropertyValue('--md-sys-color-inverse-surface')
|
||||||
|
}
|
||||||
|
return colours;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch for theme changes and update chart if needed
|
||||||
|
function setupThemeChangeListener() {
|
||||||
|
|
||||||
|
// Start observing theme changes
|
||||||
|
document.addEventListener("modeChanged", (event) => {
|
||||||
|
setTimeout(function() {
|
||||||
|
if (myChart) {
|
||||||
|
const currentLimit = document.getElementById('currentlyShowing').textContent;
|
||||||
|
const limit = (currentLimit === endpointStatsTranslations.all)
|
||||||
|
? filteredData.length
|
||||||
|
: (currentLimit === endpointStatsTranslations.top20 ? 20 : 10);
|
||||||
|
updateChart(limit);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also watch for system preference changes
|
||||||
|
window
|
||||||
|
.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
.addEventListener('change', () => {
|
||||||
|
if (myChart) {
|
||||||
|
const currentLimit = document.getElementById('currentlyShowing').textContent;
|
||||||
|
const limit = (currentLimit === endpointStatsTranslations.all)
|
||||||
|
? filteredData.length
|
||||||
|
: (currentLimit === endpointStatsTranslations.top20 ? 20 : 10);
|
||||||
|
updateChart(limit);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to filter data based on checkbox settings
|
||||||
|
function filterData() {
|
||||||
|
const includeHome = document.getElementById('hideHomeCheckbox').checked;
|
||||||
|
const includeLogin = document.getElementById('hideLoginCheckbox').checked;
|
||||||
|
|
||||||
|
filteredData = allEndpointData.filter(item => {
|
||||||
|
if (!includeHome && item.endpoint === '/') return false;
|
||||||
|
if (!includeLogin && item.endpoint === '/login') return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort and calculate
|
||||||
|
sortedData = [...filteredData].sort((a, b) => b.count - a.count);
|
||||||
|
totalEndpoints = filteredData.length;
|
||||||
|
totalVisits = filteredData.reduce((sum, item) => sum + item.count, 0);
|
||||||
|
|
||||||
|
// Update stats
|
||||||
|
document.getElementById('totalEndpoints').textContent = totalEndpoints.toLocaleString();
|
||||||
|
document.getElementById('totalVisits').textContent = totalVisits.toLocaleString();
|
||||||
|
|
||||||
|
// Update the chart with current limit
|
||||||
|
const currentLimit = document.getElementById('currentlyShowing').textContent;
|
||||||
|
const limit = (currentLimit === endpointStatsTranslations.all)
|
||||||
|
? filteredData.length
|
||||||
|
: (currentLimit === endpointStatsTranslations.top20 ? 20 : 10);
|
||||||
|
updateChart(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to fetch data from the API
|
||||||
|
async function fetchEndpointData() {
|
||||||
|
try {
|
||||||
|
// Show loading state
|
||||||
|
const chartContainer = document.querySelector('.chart-container');
|
||||||
|
const loadingDiv = document.createElement('div');
|
||||||
|
loadingDiv.className = 'loading';
|
||||||
|
loadingDiv.innerHTML = `
|
||||||
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
<span class="visually-hidden">${endpointStatsTranslations.loading}</span>
|
||||||
|
</div>`;
|
||||||
|
chartContainer.appendChild(loadingDiv);
|
||||||
|
|
||||||
|
// Also add animation to refresh button
|
||||||
|
const refreshBtn = document.getElementById('refreshBtn');
|
||||||
|
refreshBtn.classList.add('refreshing');
|
||||||
|
refreshBtn.disabled = true;
|
||||||
|
|
||||||
|
const response = await fetch('/api/v1/info/load/all');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
allEndpointData = data;
|
||||||
|
|
||||||
|
// Apply filters
|
||||||
|
filterData();
|
||||||
|
|
||||||
|
// Remove loading state
|
||||||
|
chartContainer.removeChild(loadingDiv);
|
||||||
|
refreshBtn.classList.remove('refreshing');
|
||||||
|
refreshBtn.disabled = false;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching endpoint data:', error);
|
||||||
|
// Show error message to user
|
||||||
|
showError(endpointStatsTranslations.failedToLoad);
|
||||||
|
|
||||||
|
// Reset refresh button
|
||||||
|
const refreshBtn = document.getElementById('refreshBtn');
|
||||||
|
refreshBtn.classList.remove('refreshing');
|
||||||
|
refreshBtn.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to format endpoint names
|
||||||
|
function formatEndpointName(endpoint) {
|
||||||
|
if (endpoint === '/') return endpointStatsTranslations.home;
|
||||||
|
if (endpoint === '/login') return endpointStatsTranslations.login;
|
||||||
|
return endpoint.replace('/', '').replace(/-/g, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update the table
|
||||||
|
function updateTable(data) {
|
||||||
|
const tableBody = document.getElementById('endpointTableBody');
|
||||||
|
tableBody.innerHTML = '';
|
||||||
|
|
||||||
|
data.forEach((item, index) => {
|
||||||
|
const percentage = ((item.count / totalVisits) * 100).toFixed(2);
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
|
||||||
|
// Format endpoint for better readability
|
||||||
|
let displayEndpoint = item.endpoint;
|
||||||
|
if (displayEndpoint.length > 40) {
|
||||||
|
displayEndpoint = displayEndpoint.substring(0, 37) + '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
row.innerHTML = `
|
||||||
|
<td>${index + 1}</td>
|
||||||
|
<td title="${item.endpoint}">${displayEndpoint}</td>
|
||||||
|
<td>${item.count.toLocaleString()}</td>
|
||||||
|
<td>${percentage}%</td>
|
||||||
|
`;
|
||||||
|
|
||||||
|
tableBody.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update the chart
|
||||||
|
function updateChart(dataLimit) {
|
||||||
|
const chartData = sortedData.slice(0, dataLimit);
|
||||||
|
|
||||||
|
// Calculate displayed statistics
|
||||||
|
const displayedVisits = chartData.reduce((sum, item) => sum + item.count, 0);
|
||||||
|
const displayedPercentage = totalVisits > 0
|
||||||
|
? ((displayedVisits / totalVisits) * 100).toFixed(2)
|
||||||
|
: '0';
|
||||||
|
|
||||||
|
document.getElementById('displayedVisits').textContent = displayedVisits.toLocaleString();
|
||||||
|
document.getElementById('displayedPercentage').textContent = displayedPercentage;
|
||||||
|
|
||||||
|
// If the limit equals the total filtered items, show "All"; otherwise "Top X"
|
||||||
|
document.getElementById('currentlyShowing').textContent =
|
||||||
|
(dataLimit === filteredData.length)
|
||||||
|
? endpointStatsTranslations.all
|
||||||
|
: endpointStatsTranslations.top + dataLimit;
|
||||||
|
|
||||||
|
// Update the table with new data
|
||||||
|
updateTable(chartData);
|
||||||
|
|
||||||
|
// Prepare labels and datasets
|
||||||
|
const labels = chartData.map(item => formatEndpointName(item.endpoint));
|
||||||
|
const data = chartData.map(item => item.count);
|
||||||
|
|
||||||
|
// Get theme-specific colors
|
||||||
|
const colors = getChartColors();
|
||||||
|
|
||||||
|
// Destroy previous chart if it exists
|
||||||
|
if (myChart) {
|
||||||
|
myChart.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create chart context
|
||||||
|
const ctx = document.getElementById('endpointChart').getContext('2d');
|
||||||
|
|
||||||
|
// Create new chart with theme-appropriate colors
|
||||||
|
myChart = new Chart(ctx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [{
|
||||||
|
label: endpointStatsTranslations.numberOfVisits,
|
||||||
|
data: data,
|
||||||
|
backgroundColor: colors.primaryColor.replace('rgb', 'rgba').replace(')', ', 0.6)'),
|
||||||
|
borderColor: colors.primaryColor,
|
||||||
|
borderWidth: 1
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
indexAxis: dataLimit > 20 ? 'x' : 'y',
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: 'top',
|
||||||
|
labels: {
|
||||||
|
color: colors.primaryColor,
|
||||||
|
font: {
|
||||||
|
weight: 'bold'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor: colors.tooltipBgColor,
|
||||||
|
titleColor: colors.tooltipTextColor,
|
||||||
|
bodyColor: colors.tooltipTextColor,
|
||||||
|
borderColor: colors.tooltipBgColor,
|
||||||
|
borderWidth: 1,
|
||||||
|
padding: 12,
|
||||||
|
cornerRadius: 8,
|
||||||
|
titleFont: {
|
||||||
|
size: 14,
|
||||||
|
weight: 'bold'
|
||||||
|
},
|
||||||
|
bodyFont: {
|
||||||
|
size: 13
|
||||||
|
},
|
||||||
|
callbacks: {
|
||||||
|
label: (context) => {
|
||||||
|
const value = context.raw;
|
||||||
|
const percentage = totalVisits > 0
|
||||||
|
? ((value / totalVisits) * 100).toFixed(2)
|
||||||
|
: '0';
|
||||||
|
// Insert your i18n text in the final string:
|
||||||
|
// e.g. "Visits: 12 (34% of total)"
|
||||||
|
// If your translation includes placeholders, you'd parse them here:
|
||||||
|
return endpointStatsTranslations.visitsTooltip
|
||||||
|
.replace('{0}', value.toLocaleString())
|
||||||
|
.replace('{1}', percentage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
border: {
|
||||||
|
color: colors.gridColor
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: colors.gridColor,
|
||||||
|
font: {
|
||||||
|
size: 12
|
||||||
|
},
|
||||||
|
callback: function(value, index, values) {
|
||||||
|
let label = this.getLabelForValue(value);
|
||||||
|
return label.length > 15 ? label.substr(0, 15) + '...' : label;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: `${colors.gridColor}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
border: {
|
||||||
|
color: colors.gridColor
|
||||||
|
},
|
||||||
|
min: 0,
|
||||||
|
ticks: {
|
||||||
|
color: colors.gridColor,
|
||||||
|
font: {
|
||||||
|
size: 12
|
||||||
|
},
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: `${colors.gridColor}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize with fetch and top 10
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Set up theme change listener
|
||||||
|
setupThemeChangeListener();
|
||||||
|
|
||||||
|
// Initial data fetch
|
||||||
|
fetchEndpointData();
|
||||||
|
|
||||||
|
// Set up button event listeners
|
||||||
|
document.getElementById('top10Btn').addEventListener('click', function() {
|
||||||
|
updateChart(10);
|
||||||
|
setActiveButton(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('top20Btn').addEventListener('click', function() {
|
||||||
|
updateChart(20);
|
||||||
|
setActiveButton(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('allBtn').addEventListener('click', function() {
|
||||||
|
updateChart(filteredData.length);
|
||||||
|
setActiveButton(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('refreshBtn').addEventListener('click', function() {
|
||||||
|
fetchEndpointData();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set up filter checkbox listeners
|
||||||
|
document.getElementById('hideHomeCheckbox').addEventListener('change', filterData);
|
||||||
|
document.getElementById('hideLoginCheckbox').addEventListener('change', filterData);
|
||||||
|
});
|
||||||
|
|
||||||
|
function setActiveButton(activeButton) {
|
||||||
|
// Remove active class from all buttons
|
||||||
|
document.querySelectorAll('.chart-controls button').forEach(button => {
|
||||||
|
button.classList.remove('active');
|
||||||
|
});
|
||||||
|
// Add active class to clicked button
|
||||||
|
activeButton.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to handle errors in a user-friendly way
|
||||||
|
function showError(message) {
|
||||||
|
const chartContainer = document.querySelector('.chart-container');
|
||||||
|
const errorDiv = document.createElement('div');
|
||||||
|
errorDiv.className = 'alert alert-danger';
|
||||||
|
errorDiv.innerHTML = `
|
||||||
|
<span class="material-symbols-rounded" style="vertical-align: bottom; margin-right: 5px;">error</span>
|
||||||
|
${message}
|
||||||
|
<button id="errorRetryBtn" class="btn btn-outline-danger btn-sm" style="margin-left: 10px;">
|
||||||
|
<span class="material-symbols-rounded" style="font-size: 1rem; vertical-align: bottom;">refresh</span>
|
||||||
|
${endpointStatsTranslations.retry}
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
chartContainer.innerHTML = '';
|
||||||
|
chartContainer.appendChild(errorDiv);
|
||||||
|
|
||||||
|
// Add retry button functionality
|
||||||
|
document.getElementById('errorRetryBtn').addEventListener('click', fetchEndpointData);
|
||||||
|
}
|
@ -6385,8 +6385,8 @@ function setLayerDimensions(div, viewport, mustFlip = false, mustRotate = true)
|
|||||||
const useRound = util_FeatureTest.isCSSRoundSupported;
|
const useRound = util_FeatureTest.isCSSRoundSupported;
|
||||||
const w = `var(--scale-factor) * ${pageWidth}px`,
|
const w = `var(--scale-factor) * ${pageWidth}px`,
|
||||||
h = `var(--scale-factor) * ${pageHeight}px`;
|
h = `var(--scale-factor) * ${pageHeight}px`;
|
||||||
const widthStr = useRound ? `round(up, ${w}, 1px)` : `calc(${w})`,
|
const widthStr = useRound ? `round(${w}, 1px)` : `calc(${w})`,
|
||||||
heightStr = useRound ? `round(up, ${h}, 1px)` : `calc(${h})`;
|
heightStr = useRound ? `round(${h}, 1px)` : `calc(${h})`;
|
||||||
if (!mustFlip || viewport.rotation % 180 === 0) {
|
if (!mustFlip || viewport.rotation % 180 === 0) {
|
||||||
style.width = widthStr;
|
style.width = widthStr;
|
||||||
style.height = heightStr;
|
style.height = heightStr;
|
||||||
|
@ -349,7 +349,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<div class="mb-3 mt-4 text-center">
|
<div class="mb-3 mt-4 text-center">
|
||||||
<a th:href="@{'/logout'}" role="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</a>
|
<a th:href="@{'/logout'}" role="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</a>
|
||||||
<a th:if="${role == 'ROLE_ADMIN'}" class="btn btn-info" th:href="@{'/addUsers'}" role="button" th:text="#{account.adminSettings}" target="_blank">Admin Settings</a>
|
<a th:if="${role == 'ROLE_ADMIN'}" class="btn btn-info" th:href="@{'/adminSettings'}" role="button" th:text="#{account.adminSettings}" target="_blank">Admin Settings</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,308 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
|
||||||
<head>
|
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{adminUserSettings.title}, header=#{adminUserSettings.header})}"></th:block>
|
|
||||||
<style>
|
|
||||||
.active-user {
|
|
||||||
color: green;
|
|
||||||
text-shadow: 0 0 5px green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-overflow {
|
|
||||||
max-width: 100px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow:ellipsis;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
|
||||||
<div id="page-container">
|
|
||||||
<div id="content-wrap">
|
|
||||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
|
||||||
<br><br>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-md-12 bg-card">
|
|
||||||
<div class="tool-header">
|
|
||||||
<span class="material-symbols-rounded tool-header-icon organize">manage_accounts</span>
|
|
||||||
<span class="tool-header-text" th:text="#{adminUserSettings.header}">Admin User Control Settings</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- User Settings Title -->
|
|
||||||
<div style="background: var(--md-sys-color-outline-variant);padding: .8rem; margin: 10px 0; border-radius: 2rem; text-align: center;">
|
|
||||||
<a href="#" data-bs-toggle="modal" data-bs-target="#addUserModal" class="btn btn-outline-success" th:title="#{adminUserSettings.addUser}">
|
|
||||||
<span class="material-symbols-rounded">person_add</span>
|
|
||||||
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
|
|
||||||
</a>
|
|
||||||
<a href="#" data-bs-toggle="modal" data-bs-target="#changeUserRoleModal" class="btn btn-outline-success" th:title="#{adminUserSettings.changeUserRole}">
|
|
||||||
<span class="material-symbols-rounded">edit</span>
|
|
||||||
<span th:text="#{adminUserSettings.changeUserRole}">Change User's Role</span>
|
|
||||||
</a>
|
|
||||||
<div class="my-4">
|
|
||||||
<strong th:text="#{adminUserSettings.totalUsers}">Total Users:</strong> <span th:text="${totalUsers}"></span>
|
|
||||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.activeUsers}">Active Users:</strong> <span th:text="${activeUsers}"></span>
|
|
||||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.disabledUsers}">Disabled Users:</strong> <span th:text="${disabledUsers}"></span>
|
|
||||||
<th:block th:if="${@runningEE}">
|
|
||||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalSessions}">Total Sessions:</strong> <span th:text="${sessionCount}"></span>
|
|
||||||
</th:block>
|
|
||||||
<th:block th:if="${!@runningEE}">
|
|
||||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalSessions}">Total Sessions:</strong> <span th:text="${sessionCount}"></span>/<span th:text="${maxSessions}"></span>
|
|
||||||
</th:block>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div th:if="${addMessage}" class="p-3" style="background: var(--md-sys-color-outline-variant);border-radius: 2rem; text-align: center;">
|
|
||||||
<div class="alert alert-danger mb-auto">
|
|
||||||
<span th:text="#{${addMessage}}">Default message if not found</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div th:if="${changeMessage}" class="p-3" style="background: var(--md-sys-color-outline-variant);border-radius: 2rem; text-align: center;">
|
|
||||||
<div class="alert alert-danger mb-auto">
|
|
||||||
<span th:text="#{${changeMessage}}">Default message if not found</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div th:if="${deleteMessage}" class="alert alert-danger">
|
|
||||||
<span th:text="#{${deleteMessage}}">Default message if not found</span>
|
|
||||||
</div>
|
|
||||||
<div class="bg-card mt-3 mb-3 table-responsive">
|
|
||||||
<table class="table table-striped table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">#</th>
|
|
||||||
<th scope="col" th:title="#{username}" th:text="#{username}">Username</th>
|
|
||||||
<th scope="col" th:title="#{adminUserSettings.roles}" th:text="#{adminUserSettings.roles}">Roles</th>
|
|
||||||
<th scope="col" th:title="#{adminUserSettings.authenticated}" class="text-overflow" th:text="#{adminUserSettings.authenticated}">Authenticated</th>
|
|
||||||
<th scope="col" th:title="#{adminUserSettings.lastRequest}" class="text-overflow" th:text="#{adminUserSettings.lastRequest}">Last Request</th>
|
|
||||||
<th scope="col" th:title="#{adminUserSettings.userSessions}" th:text="#{adminUserSettings.userSessions}">User Sessions</th>
|
|
||||||
<th scope="col" th:title="#{adminUserSettings.actions}" th:text="#{adminUserSettings.actions}" colspan="2">Actions</th>
|
|
||||||
<!-- <th scope="col"></th> -->
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr th:each="user : ${users}">
|
|
||||||
<th scope="row" style="align-content: center;" th:text="${user.id}"></th>
|
|
||||||
<td style="align-content: center;" th:text="${user.username}" th:classappend="${userSessions[user.username] ? 'active-user' : ''}"></td>
|
|
||||||
<td style="align-content: center;" th:text="#{${user.roleName}}"></td>
|
|
||||||
<td style="align-content: center;" th:text="${user.authenticationType}"></td>
|
|
||||||
<td style="align-content: center;" th:text="${userLastRequest[user.username] != null ? #dates.format(userLastRequest[user.username], 'yyyy-MM-dd HH:mm:ss') : 'N/A'}"></td>
|
|
||||||
<th:block th:if="${@runningEE}">
|
|
||||||
<td style="align-content: center;" th:text="${userActiveSessions[user.username] != null ? userActiveSessions[user.username] : 0}"></td>
|
|
||||||
</th:block>
|
|
||||||
<th:block th:if="${!@runningEE}">
|
|
||||||
<td style="align-content: center;" th:text="${userActiveSessions[user.username] != null ? userActiveSessions[user.username] : 0} + '/' + ${maxUserSessions}"></td>
|
|
||||||
</th:block>
|
|
||||||
|
|
||||||
<td style="align-content: center;">
|
|
||||||
<form th:if="${user.username != currentUsername}" th:action="@{'/api/v1/user/admin/deleteUser/' + ${user.username}}" method="post" onsubmit="return confirmDeleteUser()">
|
|
||||||
<button type="submit" th:title="#{adminUserSettings.deleteUser}" class="btn btn-info btn-sm"><span class="material-symbols-rounded">person_remove</span></button>
|
|
||||||
</form>
|
|
||||||
<a th:if="${user.username == currentUsername}" th:title="#{adminUserSettings.editOwnProfil}" th:href="@{'/account'}" class="btn btn-outline-success btn-sm"><span class="material-symbols-rounded">edit</span></a>
|
|
||||||
</td>
|
|
||||||
<td style="align-content: center;">
|
|
||||||
<form th:action="@{'/api/v1/user/admin/changeUserEnabled/' + ${user.username}}" method="post" onsubmit="return confirmChangeUserStatus()">
|
|
||||||
<input type="hidden" name="enabled" th:value="!${user.enabled}" />
|
|
||||||
<button type="submit" th:if="${user.enabled}" th:title="#{adminUserSettings.enabledUser}" class="btn btn-success btn-sm">
|
|
||||||
<span class="material-symbols-rounded">person</span>
|
|
||||||
</button>
|
|
||||||
<button type="submit" th:unless="${user.enabled}" th:title="#{adminUserSettings.disabledUser}" class="btn btn-danger btn-sm">
|
|
||||||
<span class="material-symbols-rounded">person_off</span>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<p th:text="#{enterpriseEdition.ssoAdvert}"></p>
|
|
||||||
|
|
||||||
<script th:inline="javascript">
|
|
||||||
const delete_confirm_text = /*[[#{adminUserSettings.confirmDeleteUser}]]*/ 'Should the user be deleted?';
|
|
||||||
const change_confirm_text = /*[[#{adminUserSettings.confirmChangeUserStatus}]]*/ 'Should the user be disabled/enabled?';
|
|
||||||
function confirmDeleteUser() {
|
|
||||||
return confirm(delete_confirm_text);
|
|
||||||
}
|
|
||||||
function confirmChangeUserStatus() {
|
|
||||||
return confirm(change_confirm_text);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- change User role Modal start -->
|
|
||||||
<div class="modal fade" id="changeUserRoleModal" tabindex="-1" aria-labelledby="changeUserRoleModalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h2 th:text="#{adminUserSettings.changeUserRole}">Change User's Role</h2>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
|
||||||
<span class="material-symbols-rounded">close</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{downgradeCurrentUserLongMessage}" th:text="#{help}">Help</button>
|
|
||||||
<form th:action="@{'/api/v1/user/admin/changeRole'}" method="post">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="username" th:text="#{username}">Username</label>
|
|
||||||
<select name="username" class="form-control" required>
|
|
||||||
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
|
||||||
<option th:each="user : ${users}" th:if="${user.username != currentUsername}" th:value="${user.username}" th:text="${user.username}">Username</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="role" th:text="#{adminUserSettings.role}">Role</label>
|
|
||||||
<select name="role" class="form-control" required>
|
|
||||||
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
|
||||||
<option th:each="roleDetail : ${roleDetails}" th:value="${roleDetail.key}" th:text="#{${roleDetail.value}}">Role</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add other fields as required -->
|
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{adminUserSettings.submit}">Save User</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- change User role Modal end -->
|
|
||||||
|
|
||||||
<!-- Add User Modal start -->
|
|
||||||
<div class="modal fade" id="addUserModal" tabindex="-1" aria-labelledby="addUserModalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="addUserModalLabel" th:text="#{adminUserSettings.addUser}">Add New User</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
|
||||||
<span class="material-symbols-rounded">close</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{adminUserSettings.usernameInfo}" th:text="#{help}">Help</button>
|
|
||||||
<form id="formsaveuser" th:action="@{'/api/v1/user/admin/saveUser'}" method="post">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="username" th:text="#{username}">Username</label>
|
|
||||||
<input type="text" class="form-control" name="username" id="username" th:title="#{adminUserSettings.usernameInfo}" required>
|
|
||||||
<span id="usernameError" style="display: none;" th:text="#{invalidUsernameMessage}">Invalid username!</span>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3" id="passwordContainer">
|
|
||||||
<label for="password" th:text="#{password}">Password</label>
|
|
||||||
<input type="password" class="form-control" name="password" id="password" required>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="role" th:text="#{adminUserSettings.role}">Role</label>
|
|
||||||
<select name="role" class="form-control" id="role" required>
|
|
||||||
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
|
||||||
<option th:each="roleDetail : ${roleDetails}" th:value="${roleDetail.key}" th:text="#{${roleDetail.value}}">Role</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="authType">Authentication Type</label>
|
|
||||||
<select id="authType" name="authType" class="form-control" required>
|
|
||||||
<option value="web" selected>WEB</option>
|
|
||||||
<option value="sso">SSO</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-check mb-3" id="checkboxContainer">
|
|
||||||
<input type="checkbox" class="form-check-input" id="forceChange" name="forceChange">
|
|
||||||
<label class="form-check-label" for="forceChange" th:text="#{adminUserSettings.forceChange}">Force user to change username/password on login</label>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{adminUserSettings.submit}">Save User</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Add User Modal end -->
|
|
||||||
|
|
||||||
<script th:inline="javascript">
|
|
||||||
jQuery.validator.addMethod("usernamePattern", function(value, element) {
|
|
||||||
// Regular expression for user name: Min. 3 characters, max. 50 characters
|
|
||||||
const regexUsername = /^[a-zA-Z0-9](?!.*[-@._+]{2,})([a-zA-Z0-9@._+-]{1,48})[a-zA-Z0-9]$/;
|
|
||||||
|
|
||||||
// Regular expression for email addresses: Max. 320 characters, with RFC-like validation
|
|
||||||
const regexEmail = /^(?=.{1,320}$)(?=.{1,64}@)[A-Za-z0-9](?:[A-Za-z0-9_.+-]*[A-Za-z0-9])?@[^-][A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*(?:\.[A-Za-z]{2,})$/;
|
|
||||||
|
|
||||||
// Check if the field is optional or meets the requirements
|
|
||||||
return this.optional(element) || regexUsername.test(value) || regexEmail.test(value);
|
|
||||||
}, /*[[#{invalidUsernameMessage}]]*/ "Invalid username format");
|
|
||||||
$(document).ready(function() {
|
|
||||||
$('[data-toggle="tooltip"]').tooltip();
|
|
||||||
|
|
||||||
$('#formsaveuser').validate({
|
|
||||||
rules: {
|
|
||||||
username: {
|
|
||||||
required: true,
|
|
||||||
usernamePattern: true
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
role: {
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
authType: {
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
messages: {
|
|
||||||
username: {
|
|
||||||
usernamePattern: /*[[#{invalidUsernameMessage}]]*/ "Invalid username format"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
errorPlacement: function(error, element) {
|
|
||||||
if (element.attr("name") === "username") {
|
|
||||||
$("#usernameError").text(error.text()).show();
|
|
||||||
} else if (element.attr("name") !== "role" && element.attr("name") !== "authType") {
|
|
||||||
error.insertAfter(element);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
success: function(label, element) {
|
|
||||||
if ($(element).attr("name") === "username") {
|
|
||||||
$("#usernameError").hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#username').on('input', function() {
|
|
||||||
var usernameInput = $(this);
|
|
||||||
var isValid = usernameInput[0].checkValidity();
|
|
||||||
var errorSpan = $('#usernameError');
|
|
||||||
|
|
||||||
if (isValid) {
|
|
||||||
usernameInput.removeClass('invalid').addClass('valid');
|
|
||||||
errorSpan.hide();
|
|
||||||
} else {
|
|
||||||
usernameInput.removeClass('valid').addClass('invalid');
|
|
||||||
errorSpan.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#authType').on('change', function() {
|
|
||||||
var authType = $(this).val();
|
|
||||||
var passwordField = $('#password');
|
|
||||||
var passwordFieldContainer = $('#passwordContainer');
|
|
||||||
var checkboxContainer = $('#checkboxContainer');
|
|
||||||
|
|
||||||
if (authType === 'sso') {
|
|
||||||
passwordField.removeAttr('required');
|
|
||||||
passwordField.prop('disabled', true).val('');
|
|
||||||
passwordFieldContainer.slideUp('fast');
|
|
||||||
checkboxContainer.slideUp('fast');
|
|
||||||
} else {
|
|
||||||
passwordField.prop('disabled', false);
|
|
||||||
passwordField.attr('required', 'required');
|
|
||||||
passwordFieldContainer.slideDown('fast');
|
|
||||||
checkboxContainer.slideDown('fast');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
360
src/main/resources/templates/adminSettings.html
Normal file
360
src/main/resources/templates/adminSettings.html
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}"
|
||||||
|
xmlns:th="https://www.thymeleaf.org">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<th:block
|
||||||
|
th:insert="~{fragments/common :: head(title=#{adminUserSettings.title}, header=#{adminUserSettings.header})}">
|
||||||
|
</th:block>
|
||||||
|
<style>
|
||||||
|
.active-user {
|
||||||
|
color: green;
|
||||||
|
text-shadow: 0 0 5px green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-overflow {
|
||||||
|
max-width: 100px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
||||||
|
<div id="page-container">
|
||||||
|
<div id="content-wrap">
|
||||||
|
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||||
|
<br><br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-12 bg-card">
|
||||||
|
<div class="tool-header">
|
||||||
|
<span class="material-symbols-rounded tool-header-icon organize">manage_accounts</span>
|
||||||
|
<span class="tool-header-text" th:text="#{adminUserSettings.header}">Admin User Control Settings</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- User Settings Title -->
|
||||||
|
<div
|
||||||
|
style="background: var(--md-sys-color-outline-variant);padding: .8rem; margin: 10px 0; border-radius: 2rem; text-align: center;">
|
||||||
|
<a href="#" th:data-bs-toggle="${@runningEE && totalUsers >= maxEnterpriseUsers} ? null : 'modal'"
|
||||||
|
th:data-bs-target="${@runningEE && totalUsers >= maxEnterpriseUsers} ? null : '#addUserModal'"
|
||||||
|
th:class="${@runningEE && totalUsers >= maxEnterpriseUsers} ? 'btn btn-danger' : 'btn btn-outline-success'"
|
||||||
|
th:title="${@runningEE && totalUsers >= maxEnterpriseUsers} ? #{adminUserSettings.maxUsersReached} : #{adminUserSettings.addUser}">
|
||||||
|
<span class="material-symbols-rounded">person_add</span>
|
||||||
|
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
|
||||||
|
</a>
|
||||||
|
<a href="#" data-bs-toggle="modal" data-bs-target="#changeUserRoleModal" class="btn btn-outline-success"
|
||||||
|
th:title="#{adminUserSettings.changeUserRole}">
|
||||||
|
<span class="material-symbols-rounded">edit</span>
|
||||||
|
<span th:text="#{adminUserSettings.changeUserRole}">Change User's Role</span>
|
||||||
|
</a>
|
||||||
|
<a href="/usage" class="btn btn-outline-success" th:title="#{adminUserSettings.usage}">
|
||||||
|
<span class="material-symbols-rounded">analytics</span>
|
||||||
|
<span th:text="#{adminUserSettings.usage}">Usage Statistics</span>
|
||||||
|
</a>
|
||||||
|
<div class="my-4">
|
||||||
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalUsers}">Total Users:</strong> <span
|
||||||
|
th:text="${totalUsers}"></span><span th:if="${@runningEE}" th:text="'/'+${maxEnterpriseUsers}"></span>
|
||||||
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.activeUsers}">Active Users:</strong>
|
||||||
|
<span th:text="${activeUsers}"></span>
|
||||||
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.disabledUsers}">Disabled Users:</strong>
|
||||||
|
<span th:text="${disabledUsers}"></span>
|
||||||
|
<th:block th:if="${@runningEE}">
|
||||||
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalSessions}">Total
|
||||||
|
Sessions:</strong> <span th:text="${sessionCount}"></span>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:if="${!@runningEE}">
|
||||||
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalSessions}">Total
|
||||||
|
Sessions:</strong> <span th:text="${sessionCount}"></span>/<span th:text="${maxSessions}"></span>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div th:if="${addMessage}" class="p-3"
|
||||||
|
style="background: var(--md-sys-color-outline-variant);border-radius: 2rem; text-align: center;">
|
||||||
|
<div class="alert alert-danger mb-auto">
|
||||||
|
<span th:text="#{${addMessage}}">Default message if not found</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div th:if="${changeMessage}" class="p-3"
|
||||||
|
style="background: var(--md-sys-color-outline-variant);border-radius: 2rem; text-align: center;">
|
||||||
|
<div class="alert alert-danger mb-auto">
|
||||||
|
<span th:text="#{${changeMessage}}">Default message if not found</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div th:if="${deleteMessage}" class="alert alert-danger">
|
||||||
|
<span th:text="#{${deleteMessage}}">Default message if not found</span>
|
||||||
|
</div>
|
||||||
|
<div class="bg-card mt-3 mb-3 table-responsive">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">#</th>
|
||||||
|
<th scope="col" th:title="#{username}" th:text="#{username}">Username</th>
|
||||||
|
<th scope="col" th:title="#{adminUserSettings.roles}" th:text="#{adminUserSettings.roles}">Roles
|
||||||
|
</th>
|
||||||
|
<th scope="col" th:title="#{adminUserSettings.authenticated}" class="text-overflow"
|
||||||
|
th:text="#{adminUserSettings.authenticated}">Authenticated</th>
|
||||||
|
<th scope="col" th:title="#{adminUserSettings.lastRequest}" class="text-overflow"
|
||||||
|
th:text="#{adminUserSettings.lastRequest}">Last Request</th>
|
||||||
|
<th scope="col" th:title="#{adminUserSettings.userSessions}"
|
||||||
|
th:text="#{adminUserSettings.userSessions}">User Sessions</th>
|
||||||
|
<th scope="col" th:title="#{adminUserSettings.actions}" th:text="#{adminUserSettings.actions}"
|
||||||
|
colspan="2">Actions</th>
|
||||||
|
<!-- <th scope="col"></th> -->
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr th:each="user : ${users}">
|
||||||
|
<th scope="row" style="align-content: center;" th:text="${user.id}"></th>
|
||||||
|
<td style="align-content: center;" th:text="${user.username}"
|
||||||
|
th:classappend="${userSessions[user.username] ? 'active-user' : ''}"></td>
|
||||||
|
<td style="align-content: center;" th:text="#{${user.roleName}}"></td>
|
||||||
|
<td style="align-content: center;" th:text="${user.authenticationType}"></td>
|
||||||
|
<td style="align-content: center;"
|
||||||
|
th:text="${userLastRequest[user.username] != null ? #dates.format(userLastRequest[user.username], 'yyyy-MM-dd HH:mm:ss') : 'N/A'}">
|
||||||
|
</td>
|
||||||
|
<th:block th:if="${@runningEE}">
|
||||||
|
<td style="align-content: center;"
|
||||||
|
th:text="${userActiveSessions[user.username] != null ? userActiveSessions[user.username] : 0}">
|
||||||
|
</td>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:if="${!@runningEE}">
|
||||||
|
<td style="align-content: center;"
|
||||||
|
th:text="${userActiveSessions[user.username] != null ? userActiveSessions[user.username] : 0} + '/' + ${maxUserSessions}">
|
||||||
|
</td>
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
<td style="align-content: center;">
|
||||||
|
<form th:if="${user.username != currentUsername}"
|
||||||
|
th:action="@{'/api/v1/user/admin/deleteUser/' + ${user.username}}" method="post"
|
||||||
|
onsubmit="return confirmDeleteUser()">
|
||||||
|
<button type="submit" th:title="#{adminUserSettings.deleteUser}"
|
||||||
|
class="btn btn-info btn-sm"><span
|
||||||
|
class="material-symbols-rounded">person_remove</span></button>
|
||||||
|
</form>
|
||||||
|
<a th:if="${user.username == currentUsername}" th:title="#{adminUserSettings.editOwnProfil}"
|
||||||
|
th:href="@{'/account'}" class="btn btn-outline-success btn-sm"><span
|
||||||
|
class="material-symbols-rounded">edit</span></a>
|
||||||
|
</td>
|
||||||
|
<td style="align-content: center;">
|
||||||
|
<form th:action="@{'/api/v1/user/admin/changeUserEnabled/' + ${user.username}}" method="post"
|
||||||
|
onsubmit="return confirmChangeUserStatus()">
|
||||||
|
<input type="hidden" name="enabled" th:value="!${user.enabled}" />
|
||||||
|
<button type="submit" th:if="${user.enabled}" th:title="#{adminUserSettings.enabledUser}"
|
||||||
|
class="btn btn-success btn-sm">
|
||||||
|
<span class="material-symbols-rounded">person</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" th:unless="${user.enabled}" th:title="#{adminUserSettings.disabledUser}"
|
||||||
|
class="btn btn-danger btn-sm">
|
||||||
|
<span class="material-symbols-rounded">person_off</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p th:if="${!@runningEE}" th:text="#{enterpriseEdition.ssoAdvert}"></p>
|
||||||
|
|
||||||
|
<script th:inline="javascript">
|
||||||
|
const delete_confirm_text = /*[[#{adminUserSettings.confirmDeleteUser}]]*/ 'Should the user be deleted?';
|
||||||
|
const change_confirm_text = /*[[#{adminUserSettings.confirmChangeUserStatus}]]*/ 'Should the user be disabled/enabled?';
|
||||||
|
function confirmDeleteUser() {
|
||||||
|
return confirm(delete_confirm_text);
|
||||||
|
}
|
||||||
|
function confirmChangeUserStatus() {
|
||||||
|
return confirm(change_confirm_text);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- change User role Modal start -->
|
||||||
|
<div class="modal fade" id="changeUserRoleModal" tabindex="-1" aria-labelledby="changeUserRoleModalLabel"
|
||||||
|
aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 th:text="#{adminUserSettings.changeUserRole}">Change User's Role</h2>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
||||||
|
<span class="material-symbols-rounded">close</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto"
|
||||||
|
th:title="#{downgradeCurrentUserLongMessage}" th:text="#{help}">Help</button>
|
||||||
|
<form th:action="@{'/api/v1/user/admin/changeRole'}" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" th:text="#{username}">Username</label>
|
||||||
|
<select name="username" class="form-control" required>
|
||||||
|
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
||||||
|
<option th:each="user : ${users}" th:if="${user.username != currentUsername}"
|
||||||
|
th:value="${user.username}" th:text="${user.username}">Username</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="role" th:text="#{adminUserSettings.role}">Role</label>
|
||||||
|
<select name="role" class="form-control" required>
|
||||||
|
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
||||||
|
<option th:each="roleDetail : ${roleDetails}" th:value="${roleDetail.key}"
|
||||||
|
th:text="#{${roleDetail.value}}">Role</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add other fields as required -->
|
||||||
|
<button type="submit" class="btn btn-primary" th:text="#{adminUserSettings.submit}">Save User</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- change User role Modal end -->
|
||||||
|
|
||||||
|
<!-- Add User Modal start -->
|
||||||
|
<div class="modal fade" id="addUserModal" tabindex="-1" aria-labelledby="addUserModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="addUserModalLabel" th:text="#{adminUserSettings.addUser}">Add New User</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
||||||
|
<span class="material-symbols-rounded">close</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto"
|
||||||
|
th:title="#{adminUserSettings.usernameInfo}" th:text="#{help}">Help</button>
|
||||||
|
<form id="formsaveuser" th:action="@{'/api/v1/user/admin/saveUser'}" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" th:text="#{username}">Username</label>
|
||||||
|
<input type="text" class="form-control" name="username" id="username"
|
||||||
|
th:title="#{adminUserSettings.usernameInfo}" required>
|
||||||
|
<span id="usernameError" style="display: none;" th:text="#{invalidUsernameMessage}">Invalid
|
||||||
|
username!</span>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3" id="passwordContainer">
|
||||||
|
<label for="password" th:text="#{password}">Password</label>
|
||||||
|
<input type="password" class="form-control" name="password" id="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="role" th:text="#{adminUserSettings.role}">Role</label>
|
||||||
|
<select name="role" class="form-control" id="role" required>
|
||||||
|
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
||||||
|
<option th:each="roleDetail : ${roleDetails}" th:value="${roleDetail.key}"
|
||||||
|
th:text="#{${roleDetail.value}}">Role</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="authType">Authentication Type</label>
|
||||||
|
<select id="authType" name="authType" class="form-control" required>
|
||||||
|
<option value="web" selected>WEB</option>
|
||||||
|
<option value="sso">SSO</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-3" id="checkboxContainer">
|
||||||
|
<input type="checkbox" class="form-check-input" id="forceChange" name="forceChange">
|
||||||
|
<label class="form-check-label" for="forceChange" th:text="#{adminUserSettings.forceChange}">Force user
|
||||||
|
to change username/password on login</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary" th:text="#{adminUserSettings.submit}">Save User</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Add User Modal end -->
|
||||||
|
|
||||||
|
<script th:inline="javascript">
|
||||||
|
jQuery.validator.addMethod("usernamePattern", function (value, element) {
|
||||||
|
// Regular expression for user name: Min. 3 characters, max. 50 characters
|
||||||
|
const regexUsername = /^[a-zA-Z0-9](?!.*[-@._+]{2,})([a-zA-Z0-9@._+-]{1,48})[a-zA-Z0-9]$/;
|
||||||
|
|
||||||
|
// Regular expression for email addresses: Max. 320 characters, with RFC-like validation
|
||||||
|
const regexEmail = /^(?=.{1,320}$)(?=.{1,64}@)[A-Za-z0-9](?:[A-Za-z0-9_.+-]*[A-Za-z0-9])?@[^-][A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*(?:\.[A-Za-z]{2,})$/;
|
||||||
|
|
||||||
|
// Check if the field is optional or meets the requirements
|
||||||
|
return this.optional(element) || regexUsername.test(value) || regexEmail.test(value);
|
||||||
|
}, /*[[#{invalidUsernameMessage}]]*/ "Invalid username format");
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
|
|
||||||
|
$('#formsaveuser').validate({
|
||||||
|
rules: {
|
||||||
|
username: {
|
||||||
|
required: true,
|
||||||
|
usernamePattern: true
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
authType: {
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
username: {
|
||||||
|
usernamePattern: /*[[#{invalidUsernameMessage}]]*/ "Invalid username format"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errorPlacement: function (error, element) {
|
||||||
|
if (element.attr("name") === "username") {
|
||||||
|
$("#usernameError").text(error.text()).show();
|
||||||
|
} else if (element.attr("name") !== "role" && element.attr("name") !== "authType") {
|
||||||
|
error.insertAfter(element);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
success: function (label, element) {
|
||||||
|
if ($(element).attr("name") === "username") {
|
||||||
|
$("#usernameError").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#username').on('input', function () {
|
||||||
|
var usernameInput = $(this);
|
||||||
|
var isValid = usernameInput[0].checkValidity();
|
||||||
|
var errorSpan = $('#usernameError');
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
usernameInput.removeClass('invalid').addClass('valid');
|
||||||
|
errorSpan.hide();
|
||||||
|
} else {
|
||||||
|
usernameInput.removeClass('valid').addClass('invalid');
|
||||||
|
errorSpan.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#authType').on('change', function () {
|
||||||
|
var authType = $(this).val();
|
||||||
|
var passwordField = $('#password');
|
||||||
|
var passwordFieldContainer = $('#passwordContainer');
|
||||||
|
var checkboxContainer = $('#checkboxContainer');
|
||||||
|
|
||||||
|
if (authType === 'sso') {
|
||||||
|
passwordField.removeAttr('required');
|
||||||
|
passwordField.prop('disabled', true).val('');
|
||||||
|
passwordFieldContainer.slideUp('fast');
|
||||||
|
checkboxContainer.slideUp('fast');
|
||||||
|
} else {
|
||||||
|
passwordField.prop('disabled', false);
|
||||||
|
passwordField.attr('required', 'required');
|
||||||
|
passwordFieldContainer.slideDown('fast');
|
||||||
|
checkboxContainer.slideDown('fast');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -29,8 +29,12 @@
|
|||||||
<label for="removeEmbeddedFiles" th:text="#{sanitizePDF.selectText.2}"></label>
|
<label for="removeEmbeddedFiles" th:text="#{sanitizePDF.selectText.2}"></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check ms-3">
|
<div class="form-check ms-3">
|
||||||
<input type="checkbox" id="removeMetadata" name="removeMetadata" checked>
|
<input type="checkbox" id="removeXMPMetadata" name="removeXMPMetadata">
|
||||||
<label for="removeMetadata" th:text="#{sanitizePDF.selectText.3}"></label>
|
<label for="removeXMPMetadata" th:text="#{sanitizePDF.selectText.3}"></label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check ms-3">
|
||||||
|
<input type="checkbox" id="removeMetadata" name="removeMetadata">
|
||||||
|
<label for="removeMetadata" th:text="#{sanitizePDF.selectText.6}"></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check ms-3">
|
<div class="form-check ms-3">
|
||||||
<input type="checkbox" id="removeLinks" name="removeLinks">
|
<input type="checkbox" id="removeLinks" name="removeLinks">
|
||||||
|
110
src/main/resources/templates/usage.html
Normal file
110
src/main/resources/templates/usage.html
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<th:block th:insert="~{fragments/common :: head(title=#{endpointStatistics.title}, header=#{endpointStatistics.header})}"></th:block>
|
||||||
|
<script th:src="@{'/js/thirdParty/chart.umd.min.js'}"></script>
|
||||||
|
<link rel="stylesheet" th:href="@{'/css/usage.css'}">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
||||||
|
<div id="page-container">
|
||||||
|
<div id="content-wrap">
|
||||||
|
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||||
|
<br><br>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-9 bg-card">
|
||||||
|
<div class="tool-header">
|
||||||
|
<span class="material-symbols-rounded tool-header-icon">analytics</span>
|
||||||
|
<span class="tool-header-text" th:text="#{endpointStatistics.header}">Endpoint Statistics</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Statistics Summary Box -->
|
||||||
|
<div class="stats-box">
|
||||||
|
<div class="chart-controls">
|
||||||
|
<button id="top10Btn" class="btn btn-outline-primary active">
|
||||||
|
<span class="material-symbols-rounded">bar_chart</span>
|
||||||
|
<span th:text="#{endpointStatistics.top10}">Top 10</span>
|
||||||
|
</button>
|
||||||
|
<button id="top20Btn" class="btn btn-outline-primary">
|
||||||
|
<span class="material-symbols-rounded">data_usage</span>
|
||||||
|
<span th:text="#{endpointStatistics.top20}">Top 20</span>
|
||||||
|
</button>
|
||||||
|
<button id="allBtn" class="btn btn-outline-primary">
|
||||||
|
<span class="material-symbols-rounded">insights</span>
|
||||||
|
<span th:text="#{endpointStatistics.all}">All</span>
|
||||||
|
</button>
|
||||||
|
<button id="refreshBtn" class="btn btn-outline-secondary">
|
||||||
|
<span class="material-symbols-rounded">refresh</span>
|
||||||
|
<span th:text="#{endpointStatistics.refresh}">Refresh</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-controls">
|
||||||
|
<label class="filter-checkbox">
|
||||||
|
<input type="checkbox" id="hideHomeCheckbox" checked>
|
||||||
|
<span th:text="#{endpointStatistics.includeHomepage}">Include Homepage ('/')</span>
|
||||||
|
</label>
|
||||||
|
<label class="filter-checkbox">
|
||||||
|
<input type="checkbox" id="hideLoginCheckbox" checked>
|
||||||
|
<span th:text="#{endpointStatistics.includeLoginPage}">Include Login Page ('/login')</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-4" style="color: var(--md-sys-color-on-surface); font-weight: 500;">
|
||||||
|
<span style="margin: 0 10px;"><strong th:text="#{endpointStatistics.totalEndpoints} + ':'">Total Endpoints:</strong> <span id="totalEndpoints">0</span></span>
|
||||||
|
<span style="margin: 0 10px;"><strong th:text="#{endpointStatistics.totalVisits} + ':'">Total Visits:</strong> <span id="totalVisits">0</span></span>
|
||||||
|
<span style="margin: 0 10px;"><strong th:text="#{endpointStatistics.showing} + ':'">Showing:</strong> <span id="currentlyShowing">Top 10</span></span>
|
||||||
|
<span style="margin: 0 10px;"><strong th:text="#{endpointStatistics.selectedVisits} + ':'">Selected Visits:</strong> <span id="displayedVisits">0</span> (<span id="displayedPercentage">0</span>%)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Chart Container -->
|
||||||
|
<div class="bg-card mt-3 mb-3">
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas id="endpointChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Table for detailed data -->
|
||||||
|
<div class="bg-card mt-3 mb-3">
|
||||||
|
<table class="endpoint-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col" style="width: 5%;">#</th>
|
||||||
|
<th scope="col" style="width: 55%;" th:text="#{endpointStatistics.endpoint}">Endpoint</th>
|
||||||
|
<th scope="col" style="width: 20%;" th:text="#{endpointStatistics.visits}">Visits</th>
|
||||||
|
<th scope="col" style="width: 20%;" th:text="#{endpointStatistics.percentage}">Percentage</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="endpointTableBody">
|
||||||
|
<!-- Table rows will be dynamically generated -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="module" th:src="@{'/js/usage.js'}"></script>
|
||||||
|
<script th:inline="javascript">
|
||||||
|
const endpointStatsTranslations = {
|
||||||
|
all: /*[[#{endpointStatistics.all}]]*/ 'All',
|
||||||
|
top20: /*[[#{endpointStatistics.top20}]]*/ 'Top 20',
|
||||||
|
loading: /*[[#{endpointStatistics.loading}]]*/ 'Loading...',
|
||||||
|
failedToLoad: /*[[#{endpointStatistics.failedToLoad}]]*/ 'Failed to load endpoint data. Please try refreshing.',
|
||||||
|
home: /*[[#{endpointStatistics.home}]]*/ 'Home',
|
||||||
|
login: /*[[#{endpointStatistics.login}]]*/ 'Login',
|
||||||
|
top: /*[[#{endpointStatistics.top}]]*/ 'Top ',
|
||||||
|
numberOfVisits: /*[[#{endpointStatistics.numberOfVisits}]]*/ 'Number of Visits',
|
||||||
|
visitsTooltip: /*[[#{endpointStatistics.visitsTooltip}]]*/ 'Visits: {0} ({1}% of total)',
|
||||||
|
retry: /*[[#{endpointStatistics.retry}]]*/ 'Retry'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -9,6 +9,7 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
import stirling.software.SPDF.config.RuntimePathConfig;
|
import stirling.software.SPDF.config.RuntimePathConfig;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.api.converters.UrlToPdfRequest;
|
import stirling.software.SPDF.model.api.converters.UrlToPdfRequest;
|
||||||
import stirling.software.SPDF.service.CustomPDFDocumentFactory;
|
import stirling.software.SPDF.service.CustomPDFDocumentFactory;
|
||||||
|
|
||||||
@ -18,12 +19,18 @@ public class ConvertWebsiteToPdfTest {
|
|||||||
|
|
||||||
@Mock private RuntimePathConfig runtimePathConfig;
|
@Mock private RuntimePathConfig runtimePathConfig;
|
||||||
|
|
||||||
|
private ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
private ConvertWebsiteToPDF convertWebsiteToPDF;
|
private ConvertWebsiteToPDF convertWebsiteToPDF;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
MockitoAnnotations.openMocks(this);
|
MockitoAnnotations.openMocks(this);
|
||||||
convertWebsiteToPDF = new ConvertWebsiteToPDF(mockPdfDocumentFactory, runtimePathConfig);
|
applicationProperties = new ApplicationProperties();
|
||||||
|
applicationProperties.getSystem().setEnableUrlToPDF(true);
|
||||||
|
convertWebsiteToPDF =
|
||||||
|
new ConvertWebsiteToPDF(
|
||||||
|
mockPdfDocumentFactory, runtimePathConfig, applicationProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -60,7 +60,6 @@
|
|||||||
/split-pdf-by-sections
|
/split-pdf-by-sections
|
||||||
/split-pdfs
|
/split-pdfs
|
||||||
/stamp
|
/stamp
|
||||||
/url-to-pdf
|
|
||||||
/validate-signature
|
/validate-signature
|
||||||
/view-pdf
|
/view-pdf
|
||||||
/swagger-ui/index.html
|
/swagger-ui/index.html
|
Loading…
x
Reference in New Issue
Block a user