diff --git a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/DataEditorForm.java b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/DataEditorForm.java index 8bcf5eddfa8..12fe08c2543 100644 --- a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/DataEditorForm.java +++ b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/DataEditorForm.java @@ -294,6 +294,9 @@ public void open(String processID, String referringView, String taskId) { } else { PrimeFaces.current().executeScript("PF('metadataLockedDialog').show();"); } + if (ServiceManager.getFileService().revertUnsavedRenamings(workpiece, process)) { + PrimeFaces.current().executeScript("PF('revertRenamingDialog').show();"); + } } catch (IOException | DAOException | InvalidImagesException | NoSuchElementException e) { Helper.setErrorMessage(e.getLocalizedMessage(), logger, e); } @@ -390,7 +393,7 @@ public String closeAndReturn() { public void close() { deleteNotSavedUploadedMedia(); unsavedDeletedMedia.clear(); - ServiceManager.getFileService().revertRenaming(filenameMapping.inverseBidiMap(), workpiece); + ServiceManager.getFileService().revertRenaming(filenameMapping.inverseBidiMap(), workpiece, process); metadataPanel.clear(); structurePanel.clear(); workpiece = null; @@ -503,6 +506,7 @@ private String save(boolean close) { filenameMapping = new DualHashBidiMap<>(); ServiceManager.getProcessService().updateChildrenFromLogicalStructure(process, workpiece.getLogicalStructure()); ServiceManager.getFileService().createBackupFile(process); + ServiceManager.getFileService().removeRenamingFile(process); try (OutputStream out = ServiceManager.getFileService().write(mainFileUri)) { ServiceManager.getMetsService().save(workpiece, out); ServiceManager.getProcessService().saveToIndex(process,false); diff --git a/Kitodo/src/main/java/org/kitodo/production/services/file/FileService.java b/Kitodo/src/main/java/org/kitodo/production/services/file/FileService.java index b258f24d26f..6024285c540 100644 --- a/Kitodo/src/main/java/org/kitodo/production/services/file/FileService.java +++ b/Kitodo/src/main/java/org/kitodo/production/services/file/FileService.java @@ -11,11 +11,15 @@ package org.kitodo.production.services.file; +import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.FileSystems; @@ -33,6 +37,7 @@ import java.util.TreeMap; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.collections4.BidiMap; import org.apache.commons.collections4.bidimap.DualHashBidiMap; @@ -97,6 +102,7 @@ public class FileService { private static final String TEMP_EXTENSION = ".tmp"; private static final String SLASH = "/"; + private static final String RENAMING = ".renaming"; /** @@ -1512,9 +1518,60 @@ public int renameMediaFiles(Process process, Workpiece workpiece, DualHashBidiMa filenameMapping.put(renamingEntry.getKey(), fileManagementModule.rename(tempFilename, newFilepath)); } } + createRenamingFile(filenameMapping, processDataUri); return numberOfRenamedMedia; } + private void createRenamingFile(BidiMap filenameMapping, URI processDir) throws IOException { + URI renamingFileUri = processDir.resolve(RENAMING); + try (OutputStream outputStream = write(renamingFileUri); + BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream))) { + String mappingContent = filenameMapping.entrySet().stream() + .map(entry -> entry.getKey() + StringUtils.SPACE + entry.getValue()) + .collect(Collectors.joining(System.lineSeparator())); + bufferedWriter.write(mappingContent); + } catch (IOException e) { + logger.error(e.getMessage()); + throw e; + } + } + + private DualHashBidiMap readRenamingFile(Process process) { + DualHashBidiMap renamingMapping = new DualHashBidiMap<>(); + URI processDataUri = ServiceManager.getProcessService().getProcessDataDirectory(process); + URI renamingFileUri = processDataUri.resolve(RENAMING); + try (InputStream inputStream = read(renamingFileUri); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { + for (String line : bufferedReader.lines().collect(Collectors.toList())) { + String[] lineFragments = line.split(StringUtils.SPACE); + if (lineFragments.length > 2) { + // TODO: should parsing stop with an error dialog? + logger.error(String.format("Unable to parse line '%s' for filename mapping", line)); + continue; + } + try { + renamingMapping.put(new URI(lineFragments[0]), new URI(lineFragments[1])); + } catch (URISyntaxException e) { + // TODO: should parsing stop with an error dialog? + logger.error(e.getMessage()); + } + } + } catch (IOException e) { + logger.info(e.getMessage()); + } + return renamingMapping; + } + + public void removeRenamingFile(Process process) throws IOException { + URI processDataUri = ServiceManager.getProcessService().getProcessDataDirectory(process); + + if (!processDataUri.toString().endsWith(SLASH)) { + processDataUri = URI.create(processDataUri + SLASH); + } + URI renamingFileUri = processDataUri.resolve(RENAMING); + delete(renamingFileUri); + } + /** * Revert renaming of media files when the user leaves the metadata editor without saving. This method uses a * provided map object to rename media files identified by the map entries values to the corresponding map entries @@ -1522,14 +1579,17 @@ public int renameMediaFiles(Process process, Workpiece workpiece, DualHashBidiMa * * @param filenameMappings Bidirectional map containing original filenames as keys and new filenames as values. * @param workpiece Workpiece of current process + * @param process Process current process */ - public void revertRenaming(BidiMap filenameMappings, Workpiece workpiece) { + public void revertRenaming(BidiMap filenameMappings, Workpiece workpiece, Process process) { // revert media variant URIs for all media files in workpiece to previous, original values for (PhysicalDivision physicalDivision : workpiece .getAllPhysicalDivisionChildrenFilteredByTypes(PhysicalDivision.TYPES)) { for (Entry mediaVariantURIEntry : physicalDivision.getMediaFiles().entrySet()) { - physicalDivision.getMediaFiles().put(mediaVariantURIEntry.getKey(), - filenameMappings.get(mediaVariantURIEntry.getValue())); + if (filenameMappings.containsKey(mediaVariantURIEntry.getValue())) { + physicalDivision.getMediaFiles().put(mediaVariantURIEntry.getKey(), + filenameMappings.get(mediaVariantURIEntry.getValue())); + } } } // revert filenames of media files to previous, original values @@ -1542,6 +1602,7 @@ public void revertRenaming(BidiMap filenameMappings, Workpiece workpie for (URI tempUri : tempUris) { fileManagementModule.rename(tempUri, StringUtils.removeEnd(tempUri.toString(), TEMP_EXTENSION)); } + removeRenamingFile(process); } catch (IOException e) { logger.error(e); } @@ -1564,4 +1625,20 @@ public DualHashBidiMap removeUnsavedUploadMediaUriFromFileMapping(URI } return updatedMap; } + + /** + * Reads potential '.renaming' file from process directory and reverts all filename changes logged in this file. + * @param workpiece the workpiece of the process + * @param process the process in question + * @return whether any file renaming information could be retrieved from a '.renaming' file of this process or not. + */ + public boolean revertUnsavedRenamings(Workpiece workpiece, Process process) { + BidiMap filenameMappings = readRenamingFile(process); + if (!filenameMappings.isEmpty()) { + revertRenaming(filenameMappings, workpiece, process); + return true; + } else { + return false; + } + } } diff --git a/Kitodo/src/main/resources/messages/messages_de.properties b/Kitodo/src/main/resources/messages/messages_de.properties index 29d3cd350c3..714a12708be 100644 --- a/Kitodo/src/main/resources/messages/messages_de.properties +++ b/Kitodo/src/main/resources/messages/messages_de.properties @@ -322,6 +322,8 @@ dataEditor.renameMedia=Medien umbenennen dataEditor.renamingMediaComplete=Das Umbenennen der Medien ist abgeschlossen dataEditor.renamingMediaError=Beim Umbenennen der Medien ist ein Fehler aufgetreten dataEditor.renamingMediaText={0} Mediendateien in {1} Ordnern wurden umbenannt. Bitte Speichern Sie den Vorgang. Andernfalls werden die Dateiumbennungen beim Schlie\u00DFen des Metadateneditors verworfen. +dataEditor.revertUnsavedRenamingTitle=Ungespeicherte Dateiumbenennungen r\u00FCckg\u00E4ngig gemacht +dataEditor.revertUnsavedRenamingText=Im Vorgangsverzeichnis wurde eine Datei '.renaming' mit Umbenennungen von Mediendateien gefunden. Dies deutet auf ein unsachgem\u00E4sses Herunterfahren des Servers hin, bevor der Vorgang mit den umbenannten Mediendateien im Editor abgespeichert wurde. Die in der Datei vorgefundenen Umbenennungen wurden daher r\u00FCckg\u00E4ngig gemacht und die '.renaming'-Datei gel\u00F6scht. dataEditor.structure.customizeDisplay=Anzeige anpassen dataEditor.structureTree.collapseAll=Strukturbaum komplett einklappen dataEditor.structureTree.expandAll=Strukturbaum komplett ausklappen diff --git a/Kitodo/src/main/resources/messages/messages_en.properties b/Kitodo/src/main/resources/messages/messages_en.properties index d6eaf2c5221..6778a110042 100644 --- a/Kitodo/src/main/resources/messages/messages_en.properties +++ b/Kitodo/src/main/resources/messages/messages_en.properties @@ -321,7 +321,9 @@ dataEditor.layoutSavedSuccessfullyText=Your current editor settings have been sa dataEditor.renameMedia=Rename media dataEditor.renamingMediaComplete=Finished renaming media dataEditor.renamingMediaError=An error occurred while renaming media files -dataEditor.renamingMediaText={0} media files in {1} folders have been renamed. Please click the 'Save' button to persist changes to the filenames. Otherwise the renaming will be reverted upon closing the editor. +dataEditor.renamingMediaText={0} media files in {1} folders have been renamed. Please click the 'Save' button to persist changes to the filenames. Otherwise, the renaming will be reverted upon closing the editor. +dataEditor.revertUnsavedRenamingTitle=Reverted filenames of unsaved renamed media files +dataEditor.revertUnsavedRenamingText=The process directory contained a file '.renaming' with filename with information about renamed media files. This indicates this process contains renamed media files but has not been saved in the metadata editor before a server shutdown occurred. Therefor the original names of the corresponding media files have been restored according to the '.renaming' file and the file itself has been removed. dataEditor.structure.customizeDisplay=Customize display dataEditor.structureTree.collapseAll=Collapse all dataEditor.structureTree.expandAll=Expand all diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/dialogs/revertRenamingDialog.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/dialogs/revertRenamingDialog.xhtml new file mode 100644 index 00000000000..8cf55d504dd --- /dev/null +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/dialogs/revertRenamingDialog.xhtml @@ -0,0 +1,46 @@ + + + + + + +

#{msgs['dataEditor.revertUnsavedRenamingTitle']}

+ + + +
+ + + +
+
+
diff --git a/Kitodo/src/main/webapp/pages/metadataEditor.xhtml b/Kitodo/src/main/webapp/pages/metadataEditor.xhtml index 9caf467410b..c9546019067 100644 --- a/Kitodo/src/main/webapp/pages/metadataEditor.xhtml +++ b/Kitodo/src/main/webapp/pages/metadataEditor.xhtml @@ -251,6 +251,7 @@ +