Compare commits

...

5 Commits

Author SHA1 Message Date
Anthony Stirling
a9b4e1eaf1 Merge branch 'main' into testCleanups 2024-08-23 12:42:08 +02:00
Ludy
b23784f598 Fix: authentication ApiKey NullPointerException (#1744) 2024-08-23 12:10:58 +02:00
Ludy
90cbcde029 Fix: Missing multi-selection and Python validation (#1740)
Missing multi-selection and Python validation
2024-08-23 09:28:06 +01:00
Anthony Stirling
382edc01f8 Multiple flag fix (#1742)
* fix

* multiple file logic cleanup

* fix

---------

Co-authored-by: a <a>
2024-08-23 09:17:50 +01:00
a
3ff4a5e5b9 cleanups! 2024-08-23 00:05:56 +01:00
84 changed files with 149 additions and 137 deletions

View File

@@ -7,6 +7,7 @@ plugins {
id "edu.sc.seis.launch4j" version "3.0.6"
id "com.diffplug.spotless" version "6.25.0"
id "com.github.jk1.dependency-license-report" version "2.9"
//id "nebula.lint" version "19.0.3"
}
import com.github.jk1.license.render.*
@@ -100,14 +101,20 @@ spotless {
}
}
//gradleLint {
// rules=['unused-dependency']
// }
tasks.wrapper {
gradleVersion = "8.7"
}
//tasks.withType(JavaCompile) {
// options.compilerArgs << "-Xlint:deprecation"
//}
configurations.all {
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
}
dependencies {
//security updates
implementation "ch.qos.logback:logback-classic:$logbackVersion"
implementation "ch.qos.logback:logback-core:$logbackVersion"
implementation "org.springframework:spring-webmvc:6.1.9"
implementation("io.github.pixee:java-security-toolkit:1.2.0")
@@ -116,36 +123,33 @@ dependencies {
implementation 'com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4'
// Exclude Tomcat and include Jetty
implementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion") {
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"
}
implementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
implementation "org.springframework.boot:spring-boot-starter-jetty:$springBootVersion"
implementation "org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion"
if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") {
implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE"
runtimeOnly "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE"
implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion"
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
//2.2.x requires rebuild of DB file.. need migration path
implementation "com.h2database:h2:2.1.214"
runtimeOnly "com.h2database:h2:2.1.214"
// implementation "com.h2database:h2:2.2.224"
}
testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
// Batik
implementation "org.apache.xmlgraphics:batik-all:1.17"
// TwelveMonkeys
implementation "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
implementation "com.twelvemonkeys.imageio:imageio-bmp:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-bmp:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-hdr:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-icns:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-iff:$imageioVersion"
implementation "com.twelvemonkeys.imageio:imageio-jpeg:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-jpeg:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-pcx:$imageioVersion@
// implementation "com.twelvemonkeys.imageio:imageio-pict:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-pnm:$imageioVersion"
@@ -153,13 +157,12 @@ dependencies {
// implementation "com.twelvemonkeys.imageio:imageio-sgi:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-tga:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-thumbsdb:$imageioVersion"
implementation "com.twelvemonkeys.imageio:imageio-tiff:$imageioVersion"
implementation "com.twelvemonkeys.imageio:imageio-webp:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-tiff:$imageioVersion"
runtimeOnly "com.twelvemonkeys.imageio:imageio-webp:$imageioVersion"
// implementation "com.twelvemonkeys.imageio:imageio-xwd:$imageioVersion"
implementation "commons-io:commons-io:2.16.1"
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0"
//general PDF
// https://mvnrepository.com/artifact/com.opencsv/opencsv
@@ -196,7 +199,7 @@ dependencies {
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
testImplementation 'org.mockito:mockito-inline:5.2.0'
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
}
tasks.withType(JavaCompile).configureEach {

View File

@@ -65,6 +65,7 @@ public class SPdfApplication {
public static void main(String[] args) throws IOException, InterruptedException {
SpringApplication app = new SpringApplication(SPdfApplication.class);
app.setAdditionalProfiles("default");
app.addInitializers(new ConfigInitializer());
Map<String, String> propertyFiles = new HashMap<>();

View File

@@ -10,7 +10,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -150,8 +149,7 @@ public class SecurityConfiguration {
})
.permitAll()
.anyRequest()
.authenticated())
.authenticationProvider(authenticationProvider());
.authenticated());
// Handle OAUTH2 Logins
if (applicationProperties.getSecurity().getOAUTH2() != null
@@ -379,14 +377,6 @@ public class SecurityConfiguration {
return new IPRateLimitingFilter(maxRequestsPerIp, maxRequestsPerIp);
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
return new JPATokenRepositoryImpl();

View File

@@ -58,7 +58,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
try {
// Use API key to authenticate. This requires you to have an authentication
// provider for API keys.
Optional<User> user = userService.loadUserByApiKey(apiKey);
Optional<User> user = userService.getUserByApiKey(apiKey);
if (!user.isPresent()) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write("Invalid API Key.");

View File

@@ -22,7 +22,6 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.DatabaseBackupInterface;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;

View File

@@ -43,7 +43,7 @@ public class ApiDocService {
Map<String, List<String>> outputToFileTypes = new HashMap<>();
public List getExtensionTypes(boolean output, String operationName) {
public List<String> getExtensionTypes(boolean output, String operationName) {
if (outputToFileTypes.size() == 0) {
outputToFileTypes.put("PDF", Arrays.asList("pdf"));
outputToFileTypes.put(

View File

@@ -9,6 +9,8 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public class UrlToPdfRequest {
@Schema(description = "The input URL to be converted to a PDF file", required = true)
@Schema(
description = "The input URL to be converted to a PDF file",
requiredMode = Schema.RequiredMode.REQUIRED)
private String urlInput;
}

View File

@@ -10,6 +10,6 @@ import stirling.software.SPDF.model.api.PDFWithPageNums;
@EqualsAndHashCode(callSuper = true)
public class ContainsTextRequest extends PDFWithPageNums {
@Schema(description = "The text to check for", required = true)
@Schema(description = "The text to check for", requiredMode = Schema.RequiredMode.REQUIRED)
private String text;
}

View File

@@ -10,6 +10,6 @@ import stirling.software.SPDF.model.api.PDFComparison;
@EqualsAndHashCode(callSuper = true)
public class FileSizeRequest extends PDFComparison {
@Schema(description = "File Size", required = true)
@Schema(description = "File Size", requiredMode = Schema.RequiredMode.REQUIRED)
private String fileSize;
}

View File

@@ -10,6 +10,6 @@ import stirling.software.SPDF.model.api.PDFComparison;
@EqualsAndHashCode(callSuper = true)
public class PageRotationRequest extends PDFComparison {
@Schema(description = "Rotation in degrees", required = true)
@Schema(description = "Rotation in degrees", requiredMode = Schema.RequiredMode.REQUIRED)
private int rotation;
}

View File

@@ -10,6 +10,6 @@ import stirling.software.SPDF.model.api.PDFComparison;
@EqualsAndHashCode(callSuper = true)
public class PageSizeRequest extends PDFComparison {
@Schema(description = "Standard Page Size", required = true)
@Schema(description = "Standard Page Size", requiredMode = Schema.RequiredMode.REQUIRED)
private String standardPageSize;
}

View File

@@ -20,13 +20,13 @@ public class OverlayPdfsRequest extends PDFFile {
@Schema(
description =
"The mode of overlaying: 'SequentialOverlay' for sequential application, 'InterleavedOverlay' for round-robin application, 'FixedRepeatOverlay' for fixed repetition based on provided counts",
required = true)
requiredMode = Schema.RequiredMode.REQUIRED)
private String overlayMode;
@Schema(
description =
"An array of integers specifying the number of times each corresponding overlay file should be applied in the 'FixedRepeatOverlay' mode. This should match the length of the overlayFiles array.",
required = false)
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private int[] counts;
@Schema(description = "Overlay position 0 is Foregound, 1 is Background")

View File

@@ -13,14 +13,14 @@ public class SplitPdfBySizeOrCountRequest extends PDFFile {
@Schema(
description =
"Determines the type of split: 0 for size, 1 for page count, 2 for document count",
required = false,
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
defaultValue = "0")
private int splitType;
@Schema(
description =
"Value for split: size in MB (e.g., '10MB') or number of pages (e.g., '5')",
required = false,
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
defaultValue = "10MB")
private String splitValue;
}

View File

@@ -15,7 +15,7 @@ public class AddStampRequest extends PDFWithPageNums {
@Schema(
description = "The stamp type (text or image)",
allowableValues = {"text", "image"},
required = true)
requiredMode = Schema.RequiredMode.REQUIRED)
private String stampType;
@Schema(description = "The stamp text")

View File

@@ -13,7 +13,7 @@ public class AutoSplitPdfRequest extends PDFFile {
@Schema(
description =
"Flag indicating if the duplex mode is active, where the page after the divider also gets removed.",
required = false,
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
defaultValue = "false")
private boolean duplexMode;
}

View File

@@ -13,7 +13,7 @@ public class ExtractHeaderRequest extends PDFFile {
@Schema(
description =
"Flag indicating whether to use the first text as a fallback if no suitable title is found. Defaults to false.",
required = false,
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
defaultValue = "false")
private boolean useFirstTextAsFallback;
}

View File

@@ -10,7 +10,9 @@ import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode
public class ExtractImageScansRequest {
@Schema(description = "The input file containing image scans", required = true)
@Schema(
description = "The input file containing image scans",
requiredMode = Schema.RequiredMode.REQUIRED)
private MultipartFile fileInput;
@Schema(

View File

@@ -10,6 +10,8 @@ import stirling.software.SPDF.model.api.PDFFile;
@EqualsAndHashCode(callSuper = true)
public class PrintFileRequest extends PDFFile {
@Schema(description = "Name of printer to match against", required = true)
@Schema(
description = "Name of printer to match against",
requiredMode = Schema.RequiredMode.REQUIRED)
private String printerName;
}

View File

@@ -15,7 +15,7 @@ public class AddWatermarkRequest extends PDFFile {
@Schema(
description = "The watermark type (text or image)",
allowableValues = {"text", "image"},
required = true)
requiredMode = Schema.RequiredMode.REQUIRED)
private String watermarkType;
@Schema(description = "The watermark text")

View File

@@ -10,6 +10,8 @@ import stirling.software.SPDF.model.api.PDFFile;
@EqualsAndHashCode(callSuper = true)
public class PDFPasswordRequest extends PDFFile {
@Schema(description = "The password of the PDF file", required = true)
@Schema(
description = "The password of the PDF file",
requiredMode = Schema.RequiredMode.REQUIRED)
private String password;
}

View File

@@ -10,7 +10,10 @@ import stirling.software.SPDF.model.api.PDFFile;
@EqualsAndHashCode(callSuper = true)
public class RedactPdfRequest extends PDFFile {
@Schema(description = "List of text to redact from the PDF", type = "string", required = true)
@Schema(
description = "List of text to redact from the PDF",
type = "string",
requiredMode = Schema.RequiredMode.REQUIRED)
private String listOfText;
@Schema(description = "Whether to use regex for the listOfText", defaultValue = "false")

View File

@@ -4,7 +4,10 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -13,8 +16,6 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.net.URL;
import java.net.HttpURLConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -73,12 +74,11 @@ public class GeneralUtils {
} catch (MalformedURLException e) {
return false;
}
}
}
public static boolean isURLReachable(String urlStr) {
try {
URL url = new URL(urlStr);
URL url = URI.create(urlStr).toURL();
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("HEAD");
int responseCode = connection.getResponseCode();
@@ -112,16 +112,19 @@ public class GeneralUtils {
sizeStr = sizeStr.replace(",", ".").replace(" ", "");
try {
if (sizeStr.endsWith("KB")) {
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024);
return (long)
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2)) * 1024);
} else if (sizeStr.endsWith("MB")) {
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024);
return (long)
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024);
} else if (sizeStr.endsWith("GB")) {
return (long) (Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024
* 1024);
return (long)
(Double.parseDouble(sizeStr.substring(0, sizeStr.length() - 2))
* 1024
* 1024
* 1024);
} else if (sizeStr.endsWith("B")) {
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
} else {
@@ -191,8 +194,7 @@ public class GeneralUtils {
// Check if the result is null or not within bounds
if (result == null || result <= 0 || result.intValue() > maxValue) {
if (n != 0)
break;
if (n != 0) break;
} else {
results.add(result.intValue());
}

View File

@@ -1,5 +1,11 @@
multipart.enabled=true
logging.level.org.springframework=WARN
logging.level.org.hibernate=WARN
logging.level.org.eclipse.jetty=WARN
logging.level.com.zaxxer.hikari=WARN
spring.jpa.open-in-view=false
server.forward-headers-strategy=NATIVE
@@ -24,10 +30,8 @@ spring.devtools.livereload.enabled=true
spring.thymeleaf.encoding=UTF-8
server.connection-timeout=${SYSTEM_CONNECTIONTIMEOUTMINUTES:20m}
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
spring.resources.static-locations=file:customFiles/static/
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
#spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/
#spring.thymeleaf.cache=false

View File

@@ -37,7 +37,7 @@ $(document).ready(function () {
try {
if (remoteCall === true) {
if (override === "multi" || (!multiple && files.length > 1 && override !== "single")) {
if (override === "multi" || (!multipleInputsForSingleRequest && files.length > 1 && override !== "single")) {
await submitMultiPdfForm(url, files);
} else {
await handleSingleDownload(url, formData);

View File

@@ -24,7 +24,7 @@
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/auto-split-pdf'}">
<p th:text="#{autoSplitPDF.formPrompt}"></p>
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
</div>
<div class="form-check ms-3">
<input type="checkbox" name="duplexMode" id="duplexMode">

View File

@@ -15,7 +15,7 @@
<div class="col-md-6">
<h2 th:text="#{BookToPDF.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/book/pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false)}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{BookToPDF.submit}"></button>
</form>
<p class="mt-3" th:text="#{BookToPDF.credit}"></p>

View File

@@ -21,7 +21,7 @@
</div>
<p th:text="#{processTimeWarning}"></p>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/file/pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false)}"></div>
<a class="btn btn-outline-primary" data-bs-toggle="collapse" href="#info" role="button"
aria-expanded="false" aria-controls="info" th:text="#{fileToPDF.supportedFileTypesInfo}"></a>
<div class="collapse" id="info">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{HTMLToPDF.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/html/pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='text/html,application/zip' )}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='text/html,application/zip' )}"></div>
<div class="mb-3">
<label for="zoom" th:text="#{HTMLToPDF.zoom}" class="form-label"></label>
<input type="number" step="0.1" class="form-control" id="zoom" name="zoom" value="1">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{imageToPDF.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/img/pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='image/*', inputText=#{imgPrompt})}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='image/*', inputText=#{imgPrompt})}"></div>
<div class="mb-3">
<label for="fitOption" th:text="#{imageToPDF.selectLabel}">Fit Options</label>
<select class="form-control" id="fitOption" name="fitOption">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{MarkdownToPDF.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/markdown/pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='text/markdown')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='text/markdown')}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{MarkdownToPDF.submit}"></button>
</form>
<p class="mt-3" th:text="#{MarkdownToPDF.help}"></p>

View File

@@ -15,7 +15,7 @@
<div class="col-md-6">
<h2 th:text="#{PDFToBook.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/book'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToBook.selectText.1}"></label>
<select class="form-control" name="outputFormat">

View File

@@ -18,7 +18,7 @@
</div>
<form id="PDFToCSVForm" th:action="@{'/api/v1/convert/pdf/csv'}" method="post" enctype="multipart/form-data">
<input id="pageId" type="hidden" name="pageId">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<button type="submit" class="btn btn-primary" th:text="#{PDFToCSV.submit}"></button>
</form>
<p id="instruction-text" style="margin: 0; display: none" th:text="#{PDFToCSV.prompt}"></p>

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{PDFToHTML.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/html'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToHTML.submit}"></button>
</form>

View File

@@ -18,8 +18,9 @@
<span class="tool-header-text" th:text="#{pdfToImage.header}"></span>
</div>
<p th:text="#{processTimeWarning}"></p>
<p th:if="${!isPython}" th:text="#{pdfToImage.info}">Python is not installed. Required for WebP conversion.</p>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/img'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{pdfToImage.selectText}"></label>
<select class="form-control" name="imageFormat">
@@ -28,7 +29,7 @@
<option value="gif">GIF</option>
<option value="tiff">TIFF</option>
<option value="bmp">BMP</option>
<option value="webp">WEPB</option>
<option th:if="${isPython}" value="webp">WEPB</option>
</select>
</div>
<div class="mb-3">

View File

@@ -19,7 +19,7 @@
</div>
<p th:text="#{pdfToPDFA.tip}"></p>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/pdfa'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="outputFormat" th:text="#{pdfToPDFA.outputFormat}"></label>
<select class="form-control" name="outputFormat" id="outputFormat">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{PDFToPresentation.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/presentation'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToPresentation.selectText.1}"></label>
<select class="form-control" name="outputFormat">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{PDFToText.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/text'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToText.selectText.1}"></label>
<select class="form-control" name="outputFormat">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{PDFToWord.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/word'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{PDFToWord.selectText.1}"></label>
<select class="form-control" name="outputFormat">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{PDFToXML.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/pdf/xml'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{PDFToXML.submit}"></button>
</form>

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{crop.header}"></span>
</div>
<form id="cropForm" th:action="@{'/api/v1/general/crop'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<input id="x" type="hidden" name="x">
<input id="y" type="hidden" name="y">
<input id="width" type="hidden" name="width">

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{pageExtracter.header}"></span>
</div>
<form th:action="@{'/api/v1/general/rearrange-pages'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<input type="hidden" id="customMode" name="customMode" value="">
<div class="mb-3">
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label>

View File

@@ -144,17 +144,18 @@
</dialog>
</th:block>
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, notRequired=${notRequired} ?: false">
<th:block th:fragment="fileSelector(name, multipleInputsForSingleRequest)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, disableMultipleFiles=${disableMultipleFiles} ?: false, notRequired=${notRequired} ?: false">
<script th:inline="javascript">
const pdfPasswordPrompt = /*[[#{error.pdfPassword}]]*/ '';
const multiple = /*[[${multiple}]]*/ false;
const multipleInputsForSingleRequest = /*[[${multipleInputsForSingleRequest}]]*/ false;
const disableMultipleFiles = /*[[${disableMultipleFiles}]]*/ false;
const remoteCall = /*[[${remoteCall}]]*/ true;
</script>
<script th:src="@{'/js/downloader.js'}"></script>
<div class="custom-file-chooser" th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
<div class="mb-3">
<input type="file" class="form-control" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" th:attr="multiple=${multiple}" th:required="${notRequired} ? null : 'required'">
<input type="file" class="form-control" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" th:attr="multiple=${!disableMultipleFiles}" th:required="${notRequired} ? null : 'required'">
</div>
<div class="selected-files"></div>
</div>

View File

@@ -20,7 +20,7 @@
<form action="api/v1/general/merge-pdfs" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{multiPdfDropPrompt}" for="fileInput-input"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=true, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=true, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<input type="checkbox" name="removeCertSign" id="removeCertSign">

View File

@@ -20,7 +20,7 @@
</div>
<!-- pdf selector -->
<div th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<script>
let originalFileName = '';
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
@@ -46,7 +46,7 @@
<div class="tab-group show-on-file-selected">
<div class="tab-container" th:title="#{addImage.upload}">
<div th:replace="~{fragments/common :: fileSelector(name='image-upload', multiple=true, accept='image/*', inputText=#{imgPrompt})}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='image-upload', multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}"></div>
<script>
const imageUpload = document.querySelector('input[name=image-upload]');
imageUpload.addEventListener('change', e => {

View File

@@ -61,7 +61,7 @@
<span class="tool-header-text" th:text="#{addPageNumbers.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/add-page-numbers'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<div class="mb-3">
<label for="customMargin" th:text="#{addPageNumbers.selectText.2}"></label>

View File

@@ -40,7 +40,7 @@
<span class="tool-header-text" th:text="#{adjustContrast.header}"></span>
</div>
<div class="col-md-8">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf', remoteCall='false')}"></div>
</div>
<br>
<canvas id="contrast-pdf-canvas"></canvas>

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{autoCrop.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/auto-crop'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{autoCrop.submit}"></button>
</form>

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{auto-rename.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/auto-rename'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{auto-rename.submit}"></button>
</form>

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{changeMetadata.header}"></span>
</div>
<form method="post" id="form1" enctype="multipart/form-data" th:action="@{'/api/v1/misc/update-metadata'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<p class="text-muted" th:text="#{changeMetadata.selectText.1}"></p>
<div class="form-check mb-3-inline ms-3">
<input type="checkbox" id="deleteAll" name="deleteAll">

View File

@@ -51,8 +51,8 @@
<span class="material-symbols-rounded tool-header-icon other">compare</span>
<span class="tool-header-text" th:text="#{compare.header}"></span>
</div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput2', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf', remoteCall='false')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput2', multipleInputsForSingleRequest=false, accept='application/pdf', remoteCall='false')}"></div>
<div class="row">
<div class="flex-container">

View File

@@ -21,7 +21,7 @@
</div>
<form action="#" th:action="@{'/api/v1/misc/compress-pdf'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
</div>
<div class="card mb-3">
<div class="card-body">

View File

@@ -21,7 +21,7 @@
<p th:if="${!isPython}" th:text="#{ScannerImageSplit.info}" class="alert alert-success text-center">Python is not installed. It is required to run.</p>
<form th:if="${isPython}" id="multiPdfForm" th:action="@{'/api/v1/misc/extract-image-scans'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='image/*, application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='image/*, application/pdf')}"></div>
<div class="mb-3">
<label for="angleThreshold" th:text="#{ScannerImageSplit.selectText.1}"></label>
<input type="number" class="form-control" id="angleThreshold" name="angle_threshold" value="10">

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{extractImages.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/misc/extract-images'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label th:text="#{extractImages.selectText}"></label>
<select class="form-control" name="format">

View File

@@ -15,7 +15,7 @@
<div class="col-md-6">
<h2 th:text="#{fakeScan.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/fake-scan'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{fakeScan.submit}"></button>
</form>

View File

@@ -19,7 +19,7 @@
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/flatten'}" id="pdfForm" class="mb-3">
<div class="custom-file">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="flattenOnlyForms" name="flattenOnlyForms">

View File

@@ -40,7 +40,7 @@
<span class="tool-header-text" th:text="#{ocr.header}"></span>
</div>
<form th:if="${#lists.size(languages) > 0}" action="#" th:action="@{'/api/v1/misc/ocr-pdf'}" method="post" enctype="multipart/form-data" class="mb-3">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="languages" class="form-label" th:text="#{ocr.selectText.1}"></label>
<hr>

View File

@@ -15,7 +15,7 @@
<div class="col-md-6">
<h2 th:text="#{printFile.header}"></h2>
<form action="#" th:action="@{'/api/v1/misc/print-file'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf,image/*')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf,image/*')}"></div>
<div class="card mb-3">
<div class="card-body">
<h4 th:text="#{printFile.selectText.1}">Select Printer</h4> <!-- Assuming the message code printFile.selectText.3 corresponds to "Select Printer" -->

View File

@@ -18,7 +18,7 @@
</div>
<form id="pdfForm" class="mb-3">
<div class="custom-file">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf', remoteCall='false')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf', remoteCall='false')}"></div>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeAnnotations.submit}"></button>
</form>

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{removeBlanks.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/misc/remove-blanks'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="threshold" th:text="#{removeBlanks.threshold}"></label>
<input type="number" class="form-control" id="threshold" name="threshold" value="10">

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{repair.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/misc/repair'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{repair.submit}"></button>
</form>
</div>

View File

@@ -26,7 +26,7 @@
<span class="tool-header-text" th:text="#{showJS.header}"></span>
</div>
<form id="pdfInfoForm" method="post" enctype="multipart/form-data" th:action="@{'/show-javascript'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, remoteCall='false', accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{showJS.submit}"></button>
</form>

View File

@@ -28,7 +28,7 @@
<span class="tool-header-text" th:text="#{AddStampRequest.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/add-stamp'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<div class="mb-3">
<label for="pageOrder" th:text="#{pageSelectionPrompt}"></label>

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{pageLayout.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/general/multi-page-layout'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="pagesPerSheet" th:text="#{pageLayout.pagesPerSheet}"></label>
<select class="form-control" id="pagesPerSheet" name="pagesPerSheet">

View File

@@ -18,8 +18,8 @@
<span class="tool-header-text" th:text="#{overlay-pdfs.header}"></span>
</div>
<form id="overlayForm" method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/overlay-pdfs'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='overlayFiles', multiple=true, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='overlayFiles', multipleInputsForSingleRequest=true, accept='application/pdf')}"></div>
<label for="overlayMode" th:text="#{overlay-pdfs.mode.label}">Overlay Mode</label>
<select id="overlayMode" name="overlayMode" class="form-control">

View File

@@ -18,7 +18,7 @@
</div>
<form th:action="@{'/api/v1/general/rearrange-pages'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="customMode" th:text="#{pdfOrganiser.mode}">Mode</label>
<select class="form-control" id="customMode" name="customMode">

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{pdfToSinglePage.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/pdf-to-single-page'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{pdfToSinglePage.submit}"></button>
</form>
</div>

View File

@@ -64,7 +64,7 @@
</div>
<div class="element-margin">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=true)}"
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=true)}"
></div>
</div>
<div class="element-margin">

View File

@@ -21,7 +21,7 @@
<span class="tool-header-text" th:text="#{removeImage.header}"></span>
</div>
<form action="api/v1/general/remove-image-pdf" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeImage.submit}"></button>

View File

@@ -18,7 +18,7 @@
</div>
<form th:action="@{'/api/v1/general/remove-pages'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="fileInput" th:text="#{pageRemover.pagesToDelete}"></label>
<input type="text" class="form-control" id="fileInput" name="pageNumbers" th:placeholder="#{pageRemover.placeholder}" required>

View File

@@ -18,7 +18,7 @@
</div>
<form action="#" th:action="@{'/api/v1/general/rotate-pdf'}" th:object="${rotateForm}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<input type="hidden" id="angleInput" name="angle" value="0">
<div id="editSection" style="display: none">

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{scalePages.header}"></span>
</div>
<form id="scalePagesFrom" th:action="@{'/api/v1/general/scale-pages'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="pageSize" th:text="#{scalePages.pageSize}"></label>
<select class="form-control" id="pageSize" name="pageSize">

View File

@@ -19,7 +19,7 @@
<form action="api/v1/security/add-password" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{addPassword.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<label th:text="#{addPassword.selectText.14}"></label> <input type="password" class="form-control" id="ownerPassword" name="ownerPassword">

View File

@@ -20,7 +20,7 @@
<form method="post" enctype="multipart/form-data" action="api/v1/security/add-watermark">
<div class="mb-3">
<label th:text="#{watermark.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3">

View File

@@ -19,7 +19,7 @@
<form action="api/v1/security/cert-sign" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{certSign.selectPDF}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<!-- Tell users to use keytool to generate JKS for other formats -->
<div class="mb-3">
@@ -37,20 +37,20 @@
<div id="pemGroup" style="display: none;">
<div class="mb-3">
<label th:text="#{certSign.selectKey}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='privateKeyFile', multiple=false, notRequired=true, accept='.pem,.der')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='privateKeyFile', multipleInputsForSingleRequest=false, notRequired=true, accept='.pem,.der')}"></div>
</div>
<div class="mb-3">
<label th:text="#{certSign.selectCert}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='certFile', multiple=false, notRequired=true, accept='.pem,.der')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='certFile', multipleInputsForSingleRequest=false, notRequired=true, accept='.pem,.der')}"></div>
</div>
</div>
<div class="mb-3" id="p12Group" style="display: none;">
<label th:text="#{certSign.selectP12}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='p12File', notRequired=true, multiple=false, accept='.p12,.pfx')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='p12File', notRequired=true, multipleInputsForSingleRequest=false, accept='.p12,.pfx')}"></div>
</div>
<div class="mb-3" id="jksGroup" style="display: none;">
<label th:text="#{certSign.selectJKS}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='jksFile', notRequired=true, multiple=false, accept='.jks,.keystore')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='jksFile', notRequired=true, multipleInputsForSingleRequest=false, accept='.jks,.keystore')}"></div>
</div>
<div class="mb-3">
<label th:text="#{certSign.password}" for="password"></label>

View File

@@ -20,7 +20,7 @@
<form action="api/v1/security/add-password" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{permissions.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<label class="mb-2" th:text="#{permissions.selectText.2}"></label>

View File

@@ -17,7 +17,7 @@
<span class="tool-header-text" th:text="#{getPdfInfo.header}"></span>
</div>
<form id="pdfInfoForm" method="post" enctype="multipart/form-data" th:action="@{'/api/v1/security/get-info-on-pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, remoteCall='false', accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, remoteCall='false', accept='application/pdf')}"></div>
<br>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{getPdfInfo.submit}"></button>
</form>

View File

@@ -19,7 +19,7 @@
<form action="api/v1/security/remove-cert-sign" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{removeCertSign.selectPDF}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3 text-center">
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeCertSign.submit}"></button>

View File

@@ -19,7 +19,7 @@
<form action="api/v1/security/remove-password" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label th:text="#{removePassword.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<label th:text="#{removePassword.selectText.2}"></label>

View File

@@ -19,7 +19,7 @@
<form method="post" enctype="multipart/form-data" action="api/v1/security/remove-watermark">
<div class="mb-3">
<label th:text="#{remove-watermark.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="mb-3">
<label for="watermarkText" th:text="#{remove-watermark.selectText.2}"></label>

View File

@@ -18,7 +18,7 @@
</div>
<form action="api/v1/security/sanitize-pdf" method="post" enctype="multipart/form-data">
<div class="mb-3">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
</div>
<div class="form-check ms-3">
<input type="checkbox" id="removeJavaScript" name="removeJavaScript" checked>

View File

@@ -36,7 +36,7 @@
</div>
<!-- pdf selector -->
<div th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='pdf-upload', multipleInputsForSingleRequest=false, disableMultipleFiles=true, accept='application/pdf')}"></div>
<script>
let originalFileName = '';
document.querySelector('input[name=pdf-upload]').addEventListener('change', async (event) => {
@@ -61,7 +61,7 @@
</script>
<div class="tab-group show-on-file-selected">
<div class="tab-container" th:title="#{sign.upload}">
<div th:replace="~{fragments/common :: fileSelector(name='image-upload', multiple=true, accept='image/*', inputText=#{imgPrompt})}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='image-upload', multipleInputsForSingleRequest=true, accept='image/*', inputText=#{imgPrompt})}"></div>
<script>
const imageUpload = document.querySelector('input[name=image-upload]');
imageUpload.addEventListener('change', e => {

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{split-by-size-or-count.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/split-by-size-or-count'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<label for="splitType" th:text="#{split-by-size-or-count.type.label}">Split Type</label>
<select id="splitType" name="splitType" class="form-control">
<option value="0" th:text="#{split-by-size-or-count.type.size}">Size</option>

View File

@@ -19,7 +19,7 @@
<span class="tool-header-text" th:text="#{split-by-sections.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/general/split-pdf-by-sections'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<label for="horizontalDivisions" th:text="#{split-by-sections.horizontal.label}">Horizontal Divisions</label>
<input type="number" id="horizontalDivisions" name="horizontalDivisions" class="form-control" min="0" max="300" value="0" required th:placeholder="#{split-by-sections.horizontal.placeholder}">
<br>

View File

@@ -20,7 +20,7 @@
</div>
<form th:action="@{'/api/v1/general/split-pages'}" method="post" enctype="multipart/form-data">
<div
th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}">
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
</div>
<div class="mb-3">