mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-11-01 01:21:18 +01:00 
			
		
		
		
	merge stuff #318
This commit is contained in:
		
							parent
							
								
									53e7dbe12f
								
							
						
					
					
						commit
						b666aa3f26
					
				@ -1,7 +1,13 @@
 | 
				
			|||||||
package stirling.software.SPDF.controller.api;
 | 
					package stirling.software.SPDF.controller.api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
 | 
					import java.nio.file.Files;
 | 
				
			||||||
 | 
					import java.nio.file.Paths;
 | 
				
			||||||
 | 
					import java.nio.file.attribute.BasicFileAttributes;
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.Comparator;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.apache.pdfbox.pdmodel.PDDocument;
 | 
					import org.apache.pdfbox.pdmodel.PDDocument;
 | 
				
			||||||
@ -11,10 +17,11 @@ import org.slf4j.Logger;
 | 
				
			|||||||
import org.slf4j.LoggerFactory;
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
import org.springframework.http.ResponseEntity;
 | 
					import org.springframework.http.ResponseEntity;
 | 
				
			||||||
import org.springframework.web.bind.annotation.PostMapping;
 | 
					import org.springframework.web.bind.annotation.PostMapping;
 | 
				
			||||||
 | 
					import org.springframework.web.bind.annotation.RequestParam;
 | 
				
			||||||
import org.springframework.web.bind.annotation.RequestPart;
 | 
					import org.springframework.web.bind.annotation.RequestPart;
 | 
				
			||||||
import org.springframework.web.bind.annotation.RestController;
 | 
					import org.springframework.web.bind.annotation.RestController;
 | 
				
			||||||
import org.springframework.web.multipart.MultipartFile;
 | 
					import org.springframework.web.multipart.MultipartFile;
 | 
				
			||||||
 | 
					import io.swagger.v3.oas.annotations.media.Schema;
 | 
				
			||||||
import io.swagger.v3.oas.annotations.Operation;
 | 
					import io.swagger.v3.oas.annotations.Operation;
 | 
				
			||||||
import io.swagger.v3.oas.annotations.Parameter;
 | 
					import io.swagger.v3.oas.annotations.Parameter;
 | 
				
			||||||
import io.swagger.v3.oas.annotations.tags.Tag;
 | 
					import io.swagger.v3.oas.annotations.tags.Tag;
 | 
				
			||||||
@ -26,55 +33,93 @@ public class MergeController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
 | 
					    private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
 | 
					 | 
				
			||||||
        // Create a new empty document
 | 
					 | 
				
			||||||
        PDDocument mergedDoc = new PDDocument();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Iterate over the list of documents and add their pages to the merged document
 | 
					private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
 | 
				
			||||||
        for (PDDocument doc : documents) {
 | 
						PDDocument mergedDoc = new PDDocument();
 | 
				
			||||||
            // Get all pages from the current document
 | 
					    for (PDDocument doc : documents) {
 | 
				
			||||||
            PDPageTree pages = doc.getPages();
 | 
					        for (PDPage page : doc.getPages()) {
 | 
				
			||||||
            // Iterate over the pages and add them to the merged document
 | 
					            mergedDoc.addPage(page);
 | 
				
			||||||
            for (PDPage page : pages) {
 | 
					 | 
				
			||||||
                mergedDoc.addPage(page);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return mergedDoc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Return the merged document
 | 
					private Comparator<MultipartFile> getSortComparator(String sortType) {
 | 
				
			||||||
        return mergedDoc;
 | 
					    switch (sortType) {
 | 
				
			||||||
 | 
					        case "byFileName":
 | 
				
			||||||
 | 
					            return Comparator.comparing(MultipartFile::getOriginalFilename);
 | 
				
			||||||
 | 
					        case "byDateModified":
 | 
				
			||||||
 | 
					            return (file1, file2) -> {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class);
 | 
				
			||||||
 | 
					                    BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class);
 | 
				
			||||||
 | 
					                    return attr1.lastModifiedTime().compareTo(attr2.lastModifiedTime());
 | 
				
			||||||
 | 
					                } catch (IOException e) {
 | 
				
			||||||
 | 
					                    return 0;  // If there's an error, treat them as equal
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        case "byDateCreated":
 | 
				
			||||||
 | 
					            return (file1, file2) -> {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    BasicFileAttributes attr1 = Files.readAttributes(Paths.get(file1.getOriginalFilename()), BasicFileAttributes.class);
 | 
				
			||||||
 | 
					                    BasicFileAttributes attr2 = Files.readAttributes(Paths.get(file2.getOriginalFilename()), BasicFileAttributes.class);
 | 
				
			||||||
 | 
					                    return attr1.creationTime().compareTo(attr2.creationTime());
 | 
				
			||||||
 | 
					                } catch (IOException e) {
 | 
				
			||||||
 | 
					                    return 0;  // If there's an error, treat them as equal
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        case "byPDFTitle":
 | 
				
			||||||
 | 
					            return (file1, file2) -> {
 | 
				
			||||||
 | 
					                try (PDDocument doc1 = PDDocument.load(file1.getInputStream());
 | 
				
			||||||
 | 
					                     PDDocument doc2 = PDDocument.load(file2.getInputStream())) {
 | 
				
			||||||
 | 
					                    String title1 = doc1.getDocumentInformation().getTitle();
 | 
				
			||||||
 | 
					                    String title2 = doc2.getDocumentInformation().getTitle();
 | 
				
			||||||
 | 
					                    return title1.compareTo(title2);
 | 
				
			||||||
 | 
					                } catch (IOException e) {
 | 
				
			||||||
 | 
					                    return 0;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        case "orderProvided":
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            return (file1, file2) -> 0;  // Default is the order provided
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@PostMapping(consumes = "multipart/form-data", value = "/merge-pdfs")
 | 
				
			||||||
 | 
					@Operation(summary = "Merge multiple PDF files into one",
 | 
				
			||||||
 | 
					        description = "This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO")
 | 
				
			||||||
 | 
					public ResponseEntity<byte[]> mergePdfs(
 | 
				
			||||||
 | 
					        @RequestPart(required = true, value = "fileInput") MultipartFile[] files,
 | 
				
			||||||
 | 
					        @RequestParam(value = "sortType", defaultValue = "orderProvided") 
 | 
				
			||||||
 | 
					        @Parameter(schema = @Schema(description = "The type of sorting to be applied on the input files before merging.",
 | 
				
			||||||
 | 
					                        allowableValues = {
 | 
				
			||||||
 | 
					                            "orderProvided", 
 | 
				
			||||||
 | 
					                            "byFileName", 
 | 
				
			||||||
 | 
					                            "byDateModified", 
 | 
				
			||||||
 | 
					                            "byDateCreated", 
 | 
				
			||||||
 | 
					                            "byPDFTitle"
 | 
				
			||||||
 | 
					                        }))
 | 
				
			||||||
 | 
					        String sortType) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Arrays.sort(files, getSortComparator(sortType));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    List<PDDocument> documents = new ArrayList<>();
 | 
				
			||||||
 | 
					    for (MultipartFile file : files) {
 | 
				
			||||||
 | 
					        try (InputStream is = file.getInputStream()) {
 | 
				
			||||||
 | 
					            documents.add(PDDocument.load(is));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @PostMapping(consumes = "multipart/form-data", value = "/merge-pdfs")
 | 
					    try (PDDocument mergedDoc = mergeDocuments(documents)) {
 | 
				
			||||||
    @Operation(
 | 
					 | 
				
			||||||
        summary = "Merge multiple PDF files into one",
 | 
					 | 
				
			||||||
        description = "This endpoint merges multiple PDF files into a single PDF file. The merged file will contain all pages from the input files in the order they were provided. Input:PDF Output:PDF Type:MISO"
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    public ResponseEntity<byte[]> mergePdfs(
 | 
					 | 
				
			||||||
        @RequestPart(required = true, value = "fileInput")
 | 
					 | 
				
			||||||
        @Parameter(description = "The input PDF files to be merged into a single file", required = true)
 | 
					 | 
				
			||||||
            MultipartFile[] files) throws IOException {
 | 
					 | 
				
			||||||
    	// Read the input PDF files into PDDocument objects
 | 
					 | 
				
			||||||
        List<PDDocument> documents = new ArrayList<>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Loop through the files array and read each file into a PDDocument
 | 
					 | 
				
			||||||
        for (MultipartFile file : files) {
 | 
					 | 
				
			||||||
            documents.add(PDDocument.load(file.getInputStream()));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        PDDocument mergedDoc = mergeDocuments(documents);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Return the merged PDF as a response
 | 
					 | 
				
			||||||
        ResponseEntity<byte[]> response = WebResponseUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
 | 
					        ResponseEntity<byte[]> response = WebResponseUtils.pdfDocToWebResponse(mergedDoc, files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_merged.pdf");
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        for (PDDocument doc : documents) {
 | 
					 | 
				
			||||||
            // Close the document after processing
 | 
					 | 
				
			||||||
            doc.close();
 | 
					 | 
				
			||||||
           }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        return response;
 | 
					        return response;
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					        for (PDDocument doc : documents) {
 | 
				
			||||||
 | 
					            if (doc != null) {
 | 
				
			||||||
 | 
					                doc.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,63 +1,113 @@
 | 
				
			|||||||
 | 
					let currentSort = {
 | 
				
			||||||
 | 
					    field: null,
 | 
				
			||||||
 | 
					    descending: false
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
document.getElementById("fileInput-input").addEventListener("change", function() {
 | 
					document.getElementById("fileInput-input").addEventListener("change", function() {
 | 
				
			||||||
	var files = this.files;
 | 
					    var files = this.files;
 | 
				
			||||||
	var list = document.getElementById("selectedFiles");
 | 
					    displayFiles(files);
 | 
				
			||||||
	list.innerHTML = "";
 | 
					});
 | 
				
			||||||
	for (var i = 0; i < files.length; i++) {
 | 
					 | 
				
			||||||
		var item = document.createElement("li");
 | 
					 | 
				
			||||||
		item.className = "list-group-item";
 | 
					 | 
				
			||||||
		item.innerHTML = `
 | 
					 | 
				
			||||||
                        	      <div class="d-flex justify-content-between align-items-center w-100">
 | 
					 | 
				
			||||||
                        	        <div class="filename">${files[i].name}</div>
 | 
					 | 
				
			||||||
                        	        <div class="arrows d-flex">
 | 
					 | 
				
			||||||
                        	          <button class="btn btn-secondary move-up"><span>↑</span></button>
 | 
					 | 
				
			||||||
                        	          <button class="btn btn-secondary move-down"><span>↓</span></button>
 | 
					 | 
				
			||||||
                        	        </div>
 | 
					 | 
				
			||||||
                        	      </div>
 | 
					 | 
				
			||||||
                        	    `;
 | 
					 | 
				
			||||||
		list.appendChild(item);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var moveUpButtons = document.querySelectorAll(".move-up");
 | 
					function displayFiles(files) {
 | 
				
			||||||
	for (var i = 0; i < moveUpButtons.length; i++) {
 | 
					    var list = document.getElementById("selectedFiles");
 | 
				
			||||||
		moveUpButtons[i].addEventListener("click", function(event) {
 | 
					    list.innerHTML = "";
 | 
				
			||||||
			event.preventDefault();
 | 
					 | 
				
			||||||
			var parent = this.closest(".list-group-item");
 | 
					 | 
				
			||||||
			var grandParent = parent.parentNode;
 | 
					 | 
				
			||||||
			if (parent.previousElementSibling) {
 | 
					 | 
				
			||||||
				grandParent.insertBefore(parent, parent.previousElementSibling);
 | 
					 | 
				
			||||||
				updateFiles();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var moveDownButtons = document.querySelectorAll(".move-down");
 | 
					    for (var i = 0; i < files.length; i++) {
 | 
				
			||||||
	for (var i = 0; i < moveDownButtons.length; i++) {
 | 
					        var item = document.createElement("li");
 | 
				
			||||||
		moveDownButtons[i].addEventListener("click", function(event) {
 | 
					        item.className = "list-group-item";
 | 
				
			||||||
			event.preventDefault();
 | 
					        item.innerHTML = `
 | 
				
			||||||
			var parent = this.closest(".list-group-item");
 | 
					            <div class="d-flex justify-content-between align-items-center w-100">
 | 
				
			||||||
			var grandParent = parent.parentNode;
 | 
					                <div class="filename">${files[i].name}</div>
 | 
				
			||||||
			if (parent.nextElementSibling) {
 | 
					                <div class="arrows d-flex">
 | 
				
			||||||
				grandParent.insertBefore(parent.nextElementSibling, parent);
 | 
					                    <button class="btn btn-secondary move-up"><span>↑</span></button>
 | 
				
			||||||
				updateFiles();
 | 
					                    <button class="btn btn-secondary move-down"><span>↓</span></button>
 | 
				
			||||||
			}
 | 
					                </div>
 | 
				
			||||||
		});
 | 
					            </div>
 | 
				
			||||||
	}
 | 
					        `;
 | 
				
			||||||
 | 
					        list.appendChild(item);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function updateFiles() {
 | 
					    attachMoveButtons();
 | 
				
			||||||
		var dataTransfer = new DataTransfer();
 | 
					}
 | 
				
			||||||
		var liElements = document.querySelectorAll("#selectedFiles li");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (var i = 0; i < liElements.length; i++) {
 | 
					function attachMoveButtons() {
 | 
				
			||||||
			var fileNameFromList = liElements[i].querySelector(".filename").innerText;
 | 
					    var moveUpButtons = document.querySelectorAll(".move-up");
 | 
				
			||||||
			var fileFromFiles;
 | 
					    for (var i = 0; i < moveUpButtons.length; i++) {
 | 
				
			||||||
			for (var j = 0; j < files.length; j++) {
 | 
					        moveUpButtons[i].addEventListener("click", function(event) {
 | 
				
			||||||
				var file = files[j];
 | 
					            event.preventDefault();
 | 
				
			||||||
				if (file.name === fileNameFromList) {
 | 
					            var parent = this.closest(".list-group-item");
 | 
				
			||||||
					dataTransfer.items.add(file);
 | 
					            var grandParent = parent.parentNode;
 | 
				
			||||||
					break;
 | 
					            if (parent.previousElementSibling) {
 | 
				
			||||||
				}
 | 
					                grandParent.insertBefore(parent, parent.previousElementSibling);
 | 
				
			||||||
			}
 | 
					                updateFiles();
 | 
				
			||||||
		}
 | 
					            }
 | 
				
			||||||
		document.getElementById("fileInput-input").files = dataTransfer.files;
 | 
					        });
 | 
				
			||||||
	}
 | 
					    }
 | 
				
			||||||
});
 | 
					
 | 
				
			||||||
 | 
					    var moveDownButtons = document.querySelectorAll(".move-down");
 | 
				
			||||||
 | 
					    for (var i = 0; i < moveDownButtons.length; i++) {
 | 
				
			||||||
 | 
					        moveDownButtons[i].addEventListener("click", function(event) {
 | 
				
			||||||
 | 
					            event.preventDefault();
 | 
				
			||||||
 | 
					            var parent = this.closest(".list-group-item");
 | 
				
			||||||
 | 
					            var grandParent = parent.parentNode;
 | 
				
			||||||
 | 
					            if (parent.nextElementSibling) {
 | 
				
			||||||
 | 
					                grandParent.insertBefore(parent.nextElementSibling, parent);
 | 
				
			||||||
 | 
					                updateFiles();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					document.getElementById("sortByNameBtn").addEventListener("click", function() {
 | 
				
			||||||
 | 
					    if (currentSort.field === "name" && !currentSort.descending) {
 | 
				
			||||||
 | 
					        currentSort.descending = true;
 | 
				
			||||||
 | 
					        sortFiles((a, b) => b.name.localeCompare(a.name));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        currentSort.field = "name";
 | 
				
			||||||
 | 
					        currentSort.descending = false;
 | 
				
			||||||
 | 
					        sortFiles((a, b) => a.name.localeCompare(b.name));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					document.getElementById("sortByDateBtn").addEventListener("click", function() {
 | 
				
			||||||
 | 
					    if (currentSort.field === "lastModified" && !currentSort.descending) {
 | 
				
			||||||
 | 
					        currentSort.descending = true;
 | 
				
			||||||
 | 
					        sortFiles((a, b) => b.lastModified - a.lastModified);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        currentSort.field = "lastModified";
 | 
				
			||||||
 | 
					        currentSort.descending = false;
 | 
				
			||||||
 | 
					        sortFiles((a, b) => a.lastModified - b.lastModified);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function sortFiles(comparator) {
 | 
				
			||||||
 | 
					    // Convert FileList to array and sort
 | 
				
			||||||
 | 
					    const sortedFilesArray = Array.from(document.getElementById("fileInput-input").files).sort(comparator);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Refresh displayed list
 | 
				
			||||||
 | 
					    displayFiles(sortedFilesArray);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Update the files property
 | 
				
			||||||
 | 
					    const dataTransfer = new DataTransfer();
 | 
				
			||||||
 | 
					    sortedFilesArray.forEach(file => dataTransfer.items.add(file));
 | 
				
			||||||
 | 
					    document.getElementById("fileInput-input").files = dataTransfer.files;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function updateFiles() {
 | 
				
			||||||
 | 
					    var dataTransfer = new DataTransfer();
 | 
				
			||||||
 | 
					    var liElements = document.querySelectorAll("#selectedFiles li");
 | 
				
			||||||
 | 
					    const files = document.getElementById("fileInput-input").files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var i = 0; i < liElements.length; i++) {
 | 
				
			||||||
 | 
					        var fileNameFromList = liElements[i].querySelector(".filename").innerText;
 | 
				
			||||||
 | 
					        var fileFromFiles;
 | 
				
			||||||
 | 
					        for (var j = 0; j < files.length; j++) {
 | 
				
			||||||
 | 
					            var file = files[j];
 | 
				
			||||||
 | 
					            if (file.name === fileNameFromList) {
 | 
				
			||||||
 | 
					                dataTransfer.items.add(file);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    document.getElementById("fileInput-input").files = dataTransfer.files;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -23,6 +23,8 @@
 | 
				
			|||||||
                                <ul id="selectedFiles" class="list-group"></ul>
 | 
					                                <ul id="selectedFiles" class="list-group"></ul>
 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
                            <div class="form-group text-center">
 | 
					                            <div class="form-group text-center">
 | 
				
			||||||
 | 
					                            	<button type="button" id="sortByNameBtn" class="btn btn-info" th:text="#{merge.sortByName}"></button>
 | 
				
			||||||
 | 
					    							<button type="button" id="sortByDateBtn" class="btn btn-info" th:text="#{merge.sortByDate}"></button>
 | 
				
			||||||
                                <button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{merge.submit}"></button>
 | 
					                                <button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{merge.submit}"></button>
 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
                        </form>
 | 
					                        </form>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user